diff --git a/addons/account/i18n/en_GB.po b/addons/account/i18n/en_GB.po index 432bfdc33a4a6f30a36ae39ae33e110314d3ebc7..cf6c13b5eaf98768799661d98d19faf218144ef9 100644 --- a/addons/account/i18n/en_GB.po +++ b/addons/account/i18n/en_GB.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-01-11 11:14+0000\n" -"PO-Revision-Date: 2011-08-25 17:24+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"PO-Revision-Date: 2011-09-13 09:07+0000\n" +"Last-Translator: John Bradshaw <Unknown>\n" "Language-Team: English (United Kingdom) <en_GB@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: 2011-09-05 05:19+0000\n" -"X-Generator: Launchpad (build 13830)\n" +"X-Launchpad-Export-Date: 2011-09-14 04:40+0000\n" +"X-Generator: Launchpad (build 13921)\n" #. module: account #: model:process.transition,name:account.process_transition_supplierreconcilepaid0 @@ -31,7 +31,7 @@ msgstr "Other Configuration" #: code:addons/account/wizard/account_open_closed_fiscalyear.py:40 #, python-format msgid "No End of year journal defined for the fiscal year" -msgstr "" +msgstr "No End of year journal defined for the fiscal year" #. module: account #: code:addons/account/account.py:516 @@ -318,7 +318,7 @@ msgstr "Allow write off" #. module: account #: view:account.analytic.chart:0 msgid "Select the Period for Analysis" -msgstr "" +msgstr "Select the Period for Analysis" #. module: account #: view:account.move.line:0 @@ -695,19 +695,19 @@ msgstr "Account Common Partner Report" #. module: account #: field:account.fiscalyear.close,period_id:0 msgid "Opening Entries Period" -msgstr "" +msgstr "Opening Entries Period" #. module: account #: model:ir.model,name:account.model_account_journal_period msgid "Journal Period" -msgstr "" +msgstr "Journal Period" #. module: account #: code:addons/account/account_move_line.py:723 #: code:addons/account/account_move_line.py:767 #, python-format msgid "To reconcile the entries company should be the same for all entries" -msgstr "" +msgstr "To reconcile the entries company should be the same for all entries" #. module: account #: view:account.account:0 @@ -719,56 +719,56 @@ msgstr "" #: model:ir.actions.act_window,name:account.action_aged_receivable #, python-format msgid "Receivable Accounts" -msgstr "" +msgstr "Accounts Receivable" #. module: account #: model:ir.model,name:account.model_account_report_general_ledger msgid "General Ledger Report" -msgstr "" +msgstr "General Ledger Report" #. module: account #: view:account.invoice:0 msgid "Re-Open" -msgstr "" +msgstr "Re-Open" #. module: account #: view:account.use.model:0 msgid "Are you sure you want to create entries?" -msgstr "" +msgstr "Are you sure you want to create entries?" #. module: account #: selection:account.bank.accounts.wizard,account_type:0 msgid "Check" -msgstr "" +msgstr "Check" #. module: account #: field:account.partner.reconcile.process,today_reconciled:0 msgid "Partners Reconciled Today" -msgstr "" +msgstr "Partners Reconciled Today" #. module: account #: selection:account.payment.term.line,value:0 #: selection:account.tax.template,type:0 msgid "Percent" -msgstr "" +msgstr "Percent" #. module: account #: model:ir.ui.menu,name:account.menu_finance_charts msgid "Charts" -msgstr "" +msgstr "Charts" #. module: account #: code:addons/account/project/wizard/project_account_analytic_line.py:47 #: model:ir.model,name:account.model_project_account_analytic_line #, python-format msgid "Analytic Entries by line" -msgstr "" +msgstr "Analytic Entries by line" #. module: account #: code:addons/account/wizard/account_change_currency.py:38 #, python-format msgid "You can only change currency for Draft Invoice !" -msgstr "" +msgstr "You can only change currency for Draft Invoice !" #. module: account #: view:account.analytic.journal:0 @@ -782,17 +782,17 @@ msgstr "" #: field:account.move.reconcile,type:0 #: field:report.invoice.created,type:0 msgid "Type" -msgstr "" +msgstr "Type" #. module: account #: model:ir.model,name:account.model_account_subscription_line msgid "Account Subscription Line" -msgstr "" +msgstr "Account Subscription Line" #. module: account #: help:account.invoice,reference:0 msgid "The partner reference of this invoice." -msgstr "" +msgstr "The partner reference of this invoice." #. module: account #: view:account.move.line.unreconcile.select:0 @@ -800,12 +800,12 @@ msgstr "" #: view:account.unreconcile.reconcile:0 #: model:ir.model,name:account.model_account_move_line_unreconcile_select msgid "Unreconciliation" -msgstr "" +msgstr "Unreconciliation" #. module: account #: model:ir.model,name:account.model_account_analytic_Journal_report msgid "Account Analytic Journal" -msgstr "" +msgstr "Account Analytic Journal" #. module: account #: model:ir.model,name:account.model_account_automatic_reconcile diff --git a/addons/account_analytic_analysis/i18n/mk.po b/addons/account_analytic_analysis/i18n/mk.po new file mode 100644 index 0000000000000000000000000000000000000000..81d5d1152072d5caec981c593d8a3617ab6ff35c --- /dev/null +++ b/addons/account_analytic_analysis/i18n/mk.po @@ -0,0 +1,296 @@ +# Macedonian translation for openobject-addons +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2011-01-11 11:14+0000\n" +"PO-Revision-Date: 2011-09-14 08:10+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Macedonian <mk@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: 2011-09-15 04:47+0000\n" +"X-Generator: Launchpad (build 13921)\n" + +#. module: account_analytic_analysis +#: help:account.analytic.account,hours_qtt_invoiced:0 +msgid "" +"Number of hours that can be invoiced plus those that already have been " +"invoiced." +msgstr "" +"Број на чаÑови кои можат да бидат фактурирани, Ð¿Ð»ÑƒÑ Ñ‡Ð°Ñови кои веќе Ñе " +"фактурирани." + +#. module: account_analytic_analysis +#: help:account.analytic.account,remaining_ca:0 +msgid "Computed using the formula: Max Invoice Price - Invoiced Amount." +msgstr "" +"ПреÑметано Ñпоред формулата: МакÑимална фактурирана цена - Фактурирана цена." + +#. module: account_analytic_analysis +#: help:account.analytic.account,remaining_hours:0 +msgid "Computed using the formula: Maximum Quantity - Hours Tot." +msgstr "ПреÑметано Ñпоред формулата: МакÑимална количина - Вкупно чаÑови" + +#. module: account_analytic_analysis +#: code:addons/account_analytic_analysis/account_analytic_analysis.py:532 +#: code:addons/account_analytic_analysis/account_analytic_analysis.py:703 +#, python-format +msgid "AccessError" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,last_invoice_date:0 +msgid "Date of the last invoice created for this analytic account." +msgstr "" + +#. module: account_analytic_analysis +#: model:ir.module.module,description:account_analytic_analysis.module_meta_information +msgid "" +"\n" +"This module is for modifying account analytic view to show\n" +"important data to project manager of services companies.\n" +"Adds menu to show relevant information to each manager..\n" +"\n" +"You can also view the report of account analytic summary\n" +"user-wise as well as month wise.\n" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,last_invoice_date:0 +msgid "Last Invoice Date" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,theorical_margin:0 +msgid "Computed using the formula: Theorial Revenue - Total Costs" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,real_margin_rate:0 +msgid "Real Margin Rate (%)" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,ca_theorical:0 +msgid "Theoretical Revenue" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,last_worked_invoiced_date:0 +msgid "" +"If invoice from the costs, this is the date of the latest work or cost that " +"have been invoiced." +msgstr "" + +#. module: account_analytic_analysis +#: model:ir.ui.menu,name:account_analytic_analysis.menu_invoicing +msgid "Billing" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,last_worked_date:0 +msgid "Date of Last Cost/Work" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,total_cost:0 +msgid "Total Costs" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,hours_quantity:0 +msgid "" +"Number of hours you spent on the analytic account (from timesheet). It " +"computes on all journal of type 'general'." +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,remaining_hours:0 +msgid "Remaining Hours" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,theorical_margin:0 +msgid "Theoretical Margin" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,ca_theorical:0 +msgid "" +"Based on the costs you had on the project, what would have been the revenue " +"if all these costs have been invoiced at the normal sale price provided by " +"the pricelist." +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,user_ids:0 +#: field:account_analytic_analysis.summary.user,user:0 +msgid "User" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,ca_to_invoice:0 +msgid "Uninvoiced Amount" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,real_margin:0 +msgid "Computed using the formula: Invoiced Amount - Total Costs." +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,hours_qtt_non_invoiced:0 +msgid "Uninvoiced Hours" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,last_worked_date:0 +msgid "Date of the latest work done on this account." +msgstr "" + +#. module: account_analytic_analysis +#: model:ir.module.module,shortdesc:account_analytic_analysis.module_meta_information +msgid "report_account_analytic" +msgstr "" + +#. module: account_analytic_analysis +#: model:ir.model,name:account_analytic_analysis.model_account_analytic_analysis_summary_user +msgid "Hours Summary by User" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,ca_invoiced:0 +msgid "Invoiced Amount" +msgstr "" + +#. module: account_analytic_analysis +#: code:addons/account_analytic_analysis/account_analytic_analysis.py:533 +#: code:addons/account_analytic_analysis/account_analytic_analysis.py:704 +#, python-format +msgid "You try to bypass an access rule (Document type: %s)." +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,last_worked_invoiced_date:0 +msgid "Date of Last Invoiced Cost" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,hours_qtt_invoiced:0 +msgid "Invoiced Hours" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,real_margin:0 +msgid "Real Margin" +msgstr "" + +#. module: account_analytic_analysis +#: constraint:account.analytic.account:0 +msgid "" +"Error! The currency has to be the same as the currency of the selected " +"company" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,ca_invoiced:0 +msgid "Total customer invoiced amount for this account." +msgstr "" + +#. module: account_analytic_analysis +#: model:ir.model,name:account_analytic_analysis.model_account_analytic_analysis_summary_month +msgid "Hours summary by month" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,real_margin_rate:0 +msgid "Computes using the formula: (Real Margin / Total Costs) * 100." +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,hours_qtt_non_invoiced:0 +msgid "" +"Number of hours (from journal of type 'general') that can be invoiced if you " +"invoice based on analytic account." +msgstr "" + +#. module: account_analytic_analysis +#: view:account.analytic.account:0 +msgid "Analytic accounts" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,remaining_ca:0 +msgid "Remaining Revenue" +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,ca_to_invoice:0 +msgid "" +"If invoice from analytic account, the remaining amount you can invoice to " +"the customer based on the total costs." +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,revenue_per_hour:0 +msgid "Computed using the formula: Invoiced Amount / Hours Tot." +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,revenue_per_hour:0 +msgid "Revenue per Hours (real)" +msgstr "" + +#. module: account_analytic_analysis +#: field:account_analytic_analysis.summary.month,unit_amount:0 +#: field:account_analytic_analysis.summary.user,unit_amount:0 +msgid "Total Time" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,month_ids:0 +#: field:account_analytic_analysis.summary.month,month:0 +msgid "Month" +msgstr "" + +#. module: account_analytic_analysis +#: field:account_analytic_analysis.summary.month,account_id:0 +#: field:account_analytic_analysis.summary.user,account_id:0 +#: model:ir.model,name:account_analytic_analysis.model_account_analytic_account +msgid "Analytic Account" +msgstr "" + +#. module: account_analytic_analysis +#: model:ir.actions.act_window,name:account_analytic_analysis.action_account_analytic_managed_overpassed +#: model:ir.ui.menu,name:account_analytic_analysis.menu_action_account_analytic_managed_overpassed +msgid "Overpassed Accounts" +msgstr "" + +#. module: account_analytic_analysis +#: model:ir.actions.act_window,name:account_analytic_analysis.action_hr_tree_invoiced_all +#: model:ir.ui.menu,name:account_analytic_analysis.menu_action_hr_tree_invoiced_all +msgid "All Uninvoiced Entries" +msgstr "" + +#. module: account_analytic_analysis +#: field:account.analytic.account,hours_quantity:0 +msgid "Hours Tot" +msgstr "" + +#. module: account_analytic_analysis +#: constraint:account.analytic.account:0 +msgid "Error! You can not create recursive analytic accounts." +msgstr "" + +#. module: account_analytic_analysis +#: help:account.analytic.account,total_cost:0 +msgid "" +"Total of costs for this account. It includes real costs (from invoices) and " +"indirect costs, like time spent on timesheets." +msgstr "" diff --git a/addons/account_coda/i18n/sl.po b/addons/account_coda/i18n/sl.po new file mode 100644 index 0000000000000000000000000000000000000000..b2c7e1447955dbbc0df118dbbb30b6aabc0109a8 --- /dev/null +++ b/addons/account_coda/i18n/sl.po @@ -0,0 +1,259 @@ +# Slovenian translation for openobject-addons +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2011-01-11 11:14+0000\n" +"PO-Revision-Date: 2011-09-14 11:29+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Slovenian <sl@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: 2011-09-15 04:47+0000\n" +"X-Generator: Launchpad (build 13921)\n" + +#. module: account_coda +#: help:account.coda,journal_id:0 +#: field:account.coda.import,journal_id:0 +msgid "Bank Journal" +msgstr "BanÄni dnevnik" + +#. module: account_coda +#: view:account.coda:0 +#: field:account.coda.import,note:0 +msgid "Log" +msgstr "Dnevnik" + +#. module: account_coda +#: model:ir.model,name:account_coda.model_account_coda_import +msgid "Account Coda Import" +msgstr "" + +#. module: account_coda +#: field:account.coda,name:0 +msgid "Coda file" +msgstr "" + +#. module: account_coda +#: view:account.coda:0 +msgid "Group By..." +msgstr "Združi po ..." + +#. module: account_coda +#: field:account.coda.import,awaiting_account:0 +msgid "Default Account for Unrecognized Movement" +msgstr "" + +#. module: account_coda +#: help:account.coda,date:0 +msgid "Import Date" +msgstr "Datum uvoza" + +#. module: account_coda +#: field:account.coda,note:0 +msgid "Import log" +msgstr "Dnevnik uvoza" + +#. module: account_coda +#: view:account.coda.import:0 +msgid "Import" +msgstr "Uvozi" + +#. module: account_coda +#: view:account.coda:0 +msgid "Coda import" +msgstr "" + +#. module: account_coda +#: code:addons/account_coda/account_coda.py:51 +#, python-format +msgid "Coda file not found for bank statement !!" +msgstr "" + +#. module: account_coda +#: help:account.coda.import,awaiting_account:0 +msgid "" +"Set here the default account that will be used, if the partner is found but " +"does not have the bank account, or if he is domiciled" +msgstr "" + +#. module: account_coda +#: view:account.coda:0 +#: field:account.coda,company_id:0 +msgid "Company" +msgstr "Podjetje" + +#. module: account_coda +#: help:account.coda.import,def_payable:0 +msgid "" +"Set here the payable account that will be used, by default, if the partner " +"is not found" +msgstr "" + +#. module: account_coda +#: view:account.coda:0 +msgid "Search Coda" +msgstr "" + +#. module: account_coda +#: view:account.coda:0 +#: field:account.coda,user_id:0 +msgid "User" +msgstr "Uporabnik" + +#. module: account_coda +#: view:account.coda:0 +#: field:account.coda,date:0 +msgid "Date" +msgstr "Datum" + +#. module: account_coda +#: model:ir.ui.menu,name:account_coda.menu_account_coda_statement +msgid "Coda Import Logs" +msgstr "" + +#. module: account_coda +#: model:ir.model,name:account_coda.model_account_coda +msgid "coda for an Account" +msgstr "" + +#. module: account_coda +#: field:account.coda.import,def_payable:0 +msgid "Default Payable Account" +msgstr "" + +#. module: account_coda +#: help:account.coda,name:0 +msgid "Store the detail of bank statements" +msgstr "" + +#. module: account_coda +#: view:account.coda.import:0 +msgid "Cancel" +msgstr "PrekliÄi" + +#. module: account_coda +#: view:account.coda.import:0 +msgid "Open Statements" +msgstr "" + +#. module: account_coda +#: code:addons/account_coda/wizard/account_coda_import.py:167 +#, python-format +msgid "The bank account %s is not defined for the partner %s.\n" +msgstr "" + +#. module: account_coda +#: model:ir.ui.menu,name:account_coda.menu_account_coda_import +msgid "Import Coda Statements" +msgstr "" + +#. module: account_coda +#: view:account.coda.import:0 +#: model:ir.actions.act_window,name:account_coda.action_account_coda_import +msgid "Import Coda Statement" +msgstr "" + +#. module: account_coda +#: model:ir.module.module,description:account_coda.module_meta_information +msgid "" +"\n" +" Module provides functionality to import\n" +" bank statements from coda files.\n" +" " +msgstr "" + +#. module: account_coda +#: view:account.coda:0 +msgid "Statements" +msgstr "Izpiski" + +#. module: account_coda +#: field:account.bank.statement,coda_id:0 +msgid "Coda" +msgstr "" + +#. module: account_coda +#: view:account.coda.import:0 +msgid "Results :" +msgstr "Rezultat" + +#. module: account_coda +#: view:account.coda.import:0 +msgid "Result of Imported Coda Statements" +msgstr "" + +#. module: account_coda +#: help:account.coda.import,def_receivable:0 +msgid "" +"Set here the receivable account that will be used, by default, if the " +"partner is not found" +msgstr "" + +#. module: account_coda +#: field:account.coda.import,coda:0 +#: model:ir.actions.act_window,name:account_coda.act_account_payment_account_bank_statement +msgid "Coda File" +msgstr "" + +#. module: account_coda +#: model:ir.model,name:account_coda.model_account_bank_statement +msgid "Bank Statement" +msgstr "BanÄni izpisek" + +#. module: account_coda +#: model:ir.actions.act_window,name:account_coda.action_account_coda +msgid "Coda Logs" +msgstr "" + +#. module: account_coda +#: code:addons/account_coda/wizard/account_coda_import.py:311 +#, python-format +msgid "Result" +msgstr "Rezultat" + +#. module: account_coda +#: view:account.coda.import:0 +msgid "Click on 'New' to select your file :" +msgstr "Kliknite na \"Novo\" za izbiro vaÅ¡e datoteke:" + +#. module: account_coda +#: field:account.coda.import,def_receivable:0 +msgid "Default Receivable Account" +msgstr "" + +#. module: account_coda +#: view:account.coda.import:0 +msgid "Close" +msgstr "Zapri" + +#. module: account_coda +#: field:account.coda,statement_ids:0 +msgid "Generated Bank Statements" +msgstr "Generirani baÄni izpiski" + +#. module: account_coda +#: model:ir.module.module,shortdesc:account_coda.module_meta_information +msgid "Account CODA - import bank statements from coda file" +msgstr "" + +#. module: account_coda +#: view:account.coda.import:0 +msgid "Configure Your Journal and Account :" +msgstr "" + +#. module: account_coda +#: view:account.coda:0 +msgid "Coda Import" +msgstr "" + +#. module: account_coda +#: view:account.coda:0 +#: field:account.coda,journal_id:0 +msgid "Journal" +msgstr "Dnevnik" diff --git a/addons/account_followup/__openerp__.py b/addons/account_followup/__openerp__.py index aef4a62c0b80b9e637eb70b602bb76c1f5861ff2..b316f5039a3db74a4894ee33979fe394adf3078f 100644 --- a/addons/account_followup/__openerp__.py +++ b/addons/account_followup/__openerp__.py @@ -45,7 +45,7 @@ Note that if you want to check the followup level for a given partner/account en 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', 'images': ['images/follow_ups.jpeg','images/send_followups.jpeg'], - 'depends': ['account_accountant'], + 'depends': ['account_accountant', 'mail'], 'init_xml': [], 'update_xml': [ 'security/ir.model.access.csv', diff --git a/addons/account_followup/account_followup_demo.xml b/addons/account_followup/account_followup_demo.xml index 2bf627de9b74ca12a73b89699856642845e8b1e8..01b275de3064aaeb820d7fc71ae75d1da5e754df 100644 --- a/addons/account_followup/account_followup_demo.xml +++ b/addons/account_followup/account_followup_demo.xml @@ -17,9 +17,9 @@ <field name="description"> Dear %(partner_name)s, -Exception made if there was a mistake of ours, it seems that the following amount staid unpaid. Please, take appropriate measures in order to carry out this payment in the next 8 days. +Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take appropriate measures in order to carry out this payment in the next 8 days. -Would your payment have been carried out after this mail was sent, please consider the present one as void. Do not hesitate to contact our accounting department at (+32).10.68.94.39. +Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to contact our accounting department at (+32).10.68.94.39. Best Regards, </field> diff --git a/addons/account_followup/wizard/account_followup_print.py b/addons/account_followup/wizard/account_followup_print.py index 18b0fc1b874aadb5a5b93bb89070567b1b60bc14..78f1b8b34c77a79b5b809e57a9b98d8b6f907acb 100644 --- a/addons/account_followup/wizard/account_followup_print.py +++ b/addons/account_followup/wizard/account_followup_print.py @@ -205,6 +205,7 @@ class account_followup_print_all(osv.osv_memory): move_obj = self.pool.get('account.move.line') user_obj = self.pool.get('res.users') line_obj = self.pool.get('account_followup.stat') + mail_message = self.pool.get('mail.message') if context is None: context = {} @@ -277,7 +278,7 @@ class account_followup_print_all(osv.osv_memory): msg = '' if dest: try: - tools.email_send(src, dest, sub, body) + mail_message.schedule_with_attach(cr, uid, src, dest, sub, body, context=context) msg_sent += partner.name + '\n' except Exception, e: raise osv.except_osv('Error !', e ) diff --git a/addons/base_action_rule/__openerp__.py b/addons/base_action_rule/__openerp__.py index b6796f8958d675686a791d950e5b1af56fdfc800..1170557efad7de4dcfba52468708c3cd19a8861f 100644 --- a/addons/base_action_rule/__openerp__.py +++ b/addons/base_action_rule/__openerp__.py @@ -37,7 +37,7 @@ trigger an automatic reminder email. """, 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', - 'depends': ['base'], + 'depends': ['base', 'mail'], 'init_xml': [ 'base_action_rule_data.xml' ], diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 2db23e75893cc7b6575121e64789104391cfd94a..805c35e4462343d17b348b971a40deafaf641077 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -24,7 +24,7 @@ from tools.translate import _ from datetime import datetime from datetime import timedelta from tools.safe_eval import safe_eval -import pooler +import pooler import re import time import tools @@ -39,7 +39,7 @@ class base_action_rule(osv.osv): _name = 'base.action.rule' _description = 'Action Rules' - + def _state_get(self, cr, uid, context=None): """ Get State @param self: The object pointer @@ -55,7 +55,7 @@ class base_action_rule(osv.osv): @param uid: the current user’s ID for security checks, @param context: A standard dictionary for contextual values """ return [('', '')] - + def priority_get(self, cr, uid, context=None): """ Get Priority @param self: The object pointer @@ -65,57 +65,57 @@ class base_action_rule(osv.osv): return [('', '')] _columns = { - 'name': fields.char('Rule Name', size=64, required=True), - 'model_id': fields.many2one('ir.model', 'Object', required=True), - 'create_date': fields.datetime('Create Date', readonly=1), + 'name': fields.char('Rule Name', size=64, required=True), + 'model_id': fields.many2one('ir.model', 'Object', required=True), + 'create_date': fields.datetime('Create Date', readonly=1), 'active': fields.boolean('Active', help="If the active field is set to False,\ - it will allow you to hide the rule without removing it."), + it will allow you to hide the rule without removing it."), 'sequence': fields.integer('Sequence', help="Gives the sequence order \ -when displaying a list of rules."), +when displaying a list of rules."), 'trg_date_type': fields.selection([ - ('none', 'None'), - ('create', 'Creation Date'), - ('action_last', 'Last Action Date'), - ('date', 'Date'), - ('deadline', 'Deadline'), - ], 'Trigger Date', size=16), + ('none', 'None'), + ('create', 'Creation Date'), + ('action_last', 'Last Action Date'), + ('date', 'Date'), + ('deadline', 'Deadline'), + ], 'Trigger Date', size=16), 'trg_date_range': fields.integer('Delay after trigger date', \ help="Delay After Trigger Date,\ specifies you can put a negative number. If you need a delay before the \ -trigger date, like sending a reminder 15 minutes before a meeting."), +trigger date, like sending a reminder 15 minutes before a meeting."), 'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'), \ - ('day', 'Days'), ('month', 'Months')], 'Delay type'), - 'trg_user_id': fields.many2one('res.users', 'Responsible'), - 'trg_partner_id': fields.many2one('res.partner', 'Partner'), - 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), - 'trg_state_from': fields.selection(_state_get, 'State', size=16), - 'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16), - - 'act_method': fields.char('Call Object Method', size=64), - 'act_user_id': fields.many2one('res.users', 'Set Responsible to'), - 'act_state': fields.selection(_state_get, 'Set State to', size=16), + ('day', 'Days'), ('month', 'Months')], 'Delay type'), + 'trg_user_id': fields.many2one('res.users', 'Responsible'), + 'trg_partner_id': fields.many2one('res.partner', 'Partner'), + 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), + 'trg_state_from': fields.selection(_state_get, 'State', size=16), + 'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16), + + 'act_method': fields.char('Call Object Method', size=64), + 'act_user_id': fields.many2one('res.users', 'Set Responsible to'), + 'act_state': fields.selection(_state_get, 'Set State to', size=16), 'act_email_cc': fields.char('Add Watchers (Cc)', size=250, help="\ These people will receive a copy of the future communication between partner \ -and users by email"), +and users by email"), 'act_remind_partner': fields.boolean('Remind Partner', help="Check \ -this if you want the rule to send a reminder by email to the partner."), +this if you want the rule to send a reminder by email to the partner."), 'act_remind_user': fields.boolean('Remind Responsible', help="Check \ -this if you want the rule to send a reminder by email to the user."), - 'act_reply_to': fields.char('Reply-To', size=64), - 'act_remind_attach': fields.boolean('Remind with Attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."), +this if you want the rule to send a reminder by email to the user."), + 'act_reply_to': fields.char('Reply-To', size=64), + 'act_remind_attach': fields.boolean('Remind with Attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."), 'act_mail_to_user': fields.boolean('Mail to Responsible', help="Check\ - this if you want the rule to send an email to the responsible person."), - 'act_mail_to_watchers': fields.boolean('Mail to Watchers (CC)', + this if you want the rule to send an email to the responsible person."), + 'act_mail_to_watchers': fields.boolean('Mail to Watchers (CC)', help="Check this if you want \ -the rule to mark CC(mail to any other person defined in actions)."), +the rule to mark CC(mail to any other person defined in actions)."), 'act_mail_to_email': fields.char('Mail to these Emails', size=128, \ - help="Email-id of the persons whom mail is to be sent"), - 'act_mail_body': fields.text('Mail body', help="Content of mail"), + help="Email-id of the persons whom mail is to be sent"), + 'act_mail_body': fields.text('Mail body', help="Content of mail"), 'regex_name': fields.char('Regex on Resource Name', size=128, help="Regular expression for matching name of the resource\ \ne.g.: 'urgent.*' will search for records having name starting with the string 'urgent'\ -\nNote: This is case sensitive search."), - 'server_action_id': fields.many2one('ir.actions.server', 'Server Action', help="Describes the action name.\neg:on which object which action to be taken on basis of which condition"), - 'filter_id':fields.many2one('ir.filters', 'Filter', required=False), +\nNote: This is case sensitive search."), + 'server_action_id': fields.many2one('ir.actions.server', 'Server Action', help="Describes the action name.\neg:on which object which action to be taken on basis of which condition"), + 'filter_id':fields.many2one('ir.filters', 'Filter', required=False), 'act_email_from' : fields.char('Email From', size=64, required=False, help="Use a python expression to specify the right field on which one than we will use for the 'From' field of the header"), 'act_email_to' : fields.char('Email To', size=64, required=False, @@ -124,17 +124,17 @@ the rule to mark CC(mail to any other person defined in actions)."), } _defaults = { - 'active': lambda *a: True, - 'trg_date_type': lambda *a: 'none', - 'trg_date_range_type': lambda *a: 'day', - 'act_mail_to_user': lambda *a: 0, - 'act_remind_partner': lambda *a: 0, - 'act_remind_user': lambda *a: 0, - 'act_mail_to_watchers': lambda *a: 0, + 'active': lambda *a: True, + 'trg_date_type': lambda *a: 'none', + 'trg_date_range_type': lambda *a: 'day', + 'act_mail_to_user': lambda *a: 0, + 'act_remind_partner': lambda *a: 0, + 'act_remind_user': lambda *a: 0, + 'act_mail_to_watchers': lambda *a: 0, } - + _order = 'sequence' - + def onchange_model_id(self, cr, uid, ids, name): #This is not a good solution as it will affect the domain only on onchange res = {'domain':{'filter_id':[]}} @@ -174,7 +174,7 @@ the rule to mark CC(mail to any other person defined in actions)."), self.pre_action(cr, uid, [new_id], model, context=context) return new_id return make_call_old - + def _write(self, old_write, model, context=None): if context is None: context = {} @@ -202,9 +202,9 @@ the rule to mark CC(mail to any other person defined in actions)."), return True def create(self, cr, uid, vals, context=None): res_id = super(base_action_rule, self).create(cr, uid, vals, context=context) - self._register_hook(cr, uid, [res_id], context=context) + self._register_hook(cr, uid, [res_id], context=context) return res_id - + def write(self, cr, uid, ids, vals, context=None): res = super(base_action_rule, self).write(cr, uid, ids, vals, context=context) self._register_hook(cr, uid, ids, context=context) @@ -268,22 +268,19 @@ the rule to mark CC(mail to any other person defined in actions)."), return body and tools.ustr(body) or '' def format_mail(self, obj, body): - """ Foramat Mail - @param self: The object pointer """ - data = { - 'object_id': obj.id, - 'object_subject': hasattr(obj, 'name') and obj.name or False, - 'object_date': hasattr(obj, 'date') and obj.date or False, - 'object_description': hasattr(obj, 'description') and obj.description or False, - 'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/', + 'object_id': obj.id, + 'object_subject': hasattr(obj, 'name') and obj.name or False, + 'object_date': hasattr(obj, 'date') and obj.date or False, + 'object_description': hasattr(obj, 'description') and obj.description or False, + 'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/', 'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and \ obj.user_id.user_email) or '/', 'object_user_phone': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and \ obj.partner_address_id.phone) or '/', - 'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/', + 'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/', 'partner_email': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and\ - obj.partner_address_id.email) or '/', + obj.partner_address_id.email) or '/', } return self.format_body(body % data) @@ -302,6 +299,7 @@ the rule to mark CC(mail to any other person defined in actions)."), if context is None: context = {} + mail_message = self.pool.get('mail.message') body = self.format_mail(obj, body) if not emailfrom: if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.user_email: @@ -311,9 +309,9 @@ the rule to mark CC(mail to any other person defined in actions)."), emailfrom = tools.ustr(emailfrom) reply_to = emailfrom if not emailfrom: - raise osv.except_osv(_('Error!'), + raise osv.except_osv(_('Error!'), _("No E-Mail ID Found for your Company address!")) - return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id)) + return mail_message.schedule_with_attach(cr, uid, emailfrom, emails, name, body, model='base.action.rule', reply_to=reply_to, res_id=obj.id) def do_check(self, cr, uid, action, obj, context=None): @@ -324,7 +322,7 @@ the rule to mark CC(mail to any other person defined in actions)."), @param context: A standard dictionary for contextual values """ if context is None: context = {} - ok = True + ok = True if action.filter_id: if action.model_id.model == action.filter_id.model_id: context.update(eval(action.filter_id.context)) @@ -479,7 +477,7 @@ the rule to mark CC(mail to any other person defined in actions)."), return True _constraints = [ - (_check_mail, 'Error: The mail is not well formated', ['act_mail_body']), + (_check_mail, 'Error: The mail is not well formated', ['act_mail_body']), ] base_action_rule() diff --git a/addons/mail_gateway/__openerp__.py b/addons/base_calendar/__openerp__.py similarity index 57% rename from addons/mail_gateway/__openerp__.py rename to addons/base_calendar/__openerp__.py index af1e28e0c2c1c28b30c120ed04e0820c074d2834..6edb978301f3059c86c6520d3f36d0b710cdb4b1 100644 --- a/addons/mail_gateway/__openerp__.py +++ b/addons/base_calendar/__openerp__.py @@ -20,30 +20,37 @@ ############################################################################## { - 'name': 'Email Gateway System', - 'version': '1.0', - 'category': 'Tools', + "name" : "Basic Calendar Functionality", + "version" : "1.0", + "depends" : ["base", "mail"], 'complexity': "easy", 'description': """ -The generic email gateway system allows to send and receive emails. -=================================================================== +This is a full-featured calendar system. +======================================== - * History of emails - * Easy integration with any module""", - 'author': 'OpenERP SA', +It supports: + - Calendar of events + - Alerts (create requests) + - Recurring events + - Invitations to people""", + "author" : "OpenERP SA", + 'category': 'Tools', 'website': 'http://www.openerp.com', - 'depends': ['base'], - 'init_xml': [], - 'update_xml': [ - "mail_gateway_view.xml", - "res_partner_view.xml", - 'security/ir.model.access.csv' - + "init_xml" : [ + 'base_calendar_data.xml' ], - 'demo_xml': [], - 'installable': True, - 'active': False, - 'certificate': '001056784984222247309', - 'images': ['images/customer_history.jpeg','images/messages_form.jpeg','images/messages_list.jpeg'], + "demo_xml" : [], + "update_xml" : [ + 'security/calendar_security.xml', + 'security/ir.model.access.csv', + 'wizard/base_calendar_invite_attendee_view.xml', + 'base_calendar_view.xml' + ], + "test" : ['test/base_calendar_test.yml'], + "installable" : True, + "active" : False, + "certificate" : "00694071962960352821", + 'images': ['images/base_calendar1.jpeg','images/base_calendar2.jpeg','images/base_calendar3.jpeg','images/base_calendar4.jpeg',], } + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index a128e18fcc1c375f2066ab32eabf25d66bcd0d93..e020984a64726fd903dda5be8a52a60f1735b7f5 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -484,6 +484,7 @@ property or property parameter."), context = {} company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.name + mail_message = self.pool.get('mail.message') for att in self.browse(cr, uid, ids, context=context): sign = att.sent_by_uid and att.sent_by_uid.signature or '' sign = '<br>'.join(sign and sign.split('\n') or []) @@ -510,14 +511,15 @@ property or property parameter."), body = html_invitation % body_vals if mail_to and email_from: attach = self.get_ics_file(cr, uid, res_obj, context=context) - tools.email_send( + mail_message.schedule_with_attach(cr, uid, email_from, mail_to, sub, body, - attach=attach and [('invitation.ics', attach)] or None, + attachments=attach and {'invitation.ics': attach} or None, subtype='html', - reply_to=email_from + reply_to=email_from, + context=context ) return True @@ -812,6 +814,7 @@ class calendar_alarm(osv.osv): """ if context is None: context = {} + mail_message = self.pool.get('mail.message') current_datetime = datetime.now() request_obj = self.pool.get('res.request') alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context) @@ -893,11 +896,12 @@ From: for att in alarm.attendee_ids: mail_to.append(att.user_id.user_email) if mail_to: - tools.email_send( + mail_message.schedule_with_attach(cr, uid, tools.config.get('email_from', False), mail_to, sub, - body + body, + context=context ) if next_trigger_date: update_vals.update({'trigger_date': next_trigger_date}) @@ -1038,7 +1042,7 @@ rule or repeating pattern of time to exclude from the recurring rule."), 'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}), 'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with Organizer Attribure of VEvent. 'organizer_id': fields.many2one('res.users', 'Organizer', states={'done': [('readonly', True)]}), - 'end_type' : fields.selection([('count', 'Fix amout of times'), ('end_date','End date')], 'Way to end reccurency'), + 'end_type' : fields.selection([('count', 'Number of repetitions'), ('end_date','End date')], 'Recurrence termination'), 'interval': fields.integer('Repeat every', help="Repeat every (Days/Week/Month/Year)"), 'count': fields.integer('Repeat', help="Repeat x times"), 'mo': fields.boolean('Mon'), @@ -1199,13 +1203,14 @@ rule or repeating pattern of time to exclude from the recurring rule."), return (datas.get('end_type') == 'count' and (';COUNT=' + str(datas.get('count'))) or '') +\ ((datas.get('end_date_new') and datas.get('end_type') == 'end_date' and (';UNTIL=' + datas.get('end_date_new'))) or '') - + freq=datas.get('rrule_type') if freq == 'none': return '' + interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or '' - return 'FREQ=' + freq.upper() + get_week_string(freq, datas) + interval_srting + get_end_date(datas) + get_month_string(freq, datas) + return 'FREQ=' + freq.upper() + get_week_string(freq, datas) + interval_srting + get_end_date(datas) + get_month_string(freq, datas) def remove_virtual_id(self, ids): if isinstance(ids, (str, int)): @@ -1216,9 +1221,8 @@ rule or repeating pattern of time to exclude from the recurring rule."), for id in ids: res.append(base_calendar_id2real_id(id)) return res - - def search(self, cr, uid, args, offset=0, limit=0, order=None, - context=None, count=False): + + def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False): args_without_date = [] start_date = False until_date = False @@ -1250,7 +1254,6 @@ rule or repeating pattern of time to exclude from the recurring rule."), else: return res - def get_edit_all(self, cr, uid, id, vals=None): """ return true if we have to edit all meeting from the same recurrent @@ -1277,7 +1280,8 @@ rule or repeating pattern of time to exclude from the recurring rule."), return date_start == split_id[1] except Exception: return True - + + def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True): if context is None: context = {} @@ -1289,7 +1293,6 @@ rule or repeating pattern of time to exclude from the recurring rule."), res = False for event_id in select: real_event_id = base_calendar_id2real_id(event_id) - edit_all = self.get_edit_all(cr, uid, event_id, vals=vals) if edit_all: if self.need_to_update(event_id, vals): diff --git a/addons/base_crypt/i18n/sl.po b/addons/base_crypt/i18n/sl.po new file mode 100644 index 0000000000000000000000000000000000000000..d52c5723ba990e9d7612ce1d2f2f8b20989c879c --- /dev/null +++ b/addons/base_crypt/i18n/sl.po @@ -0,0 +1,84 @@ +# Slovenian translation for openobject-addons +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2011-01-11 11:14+0000\n" +"PO-Revision-Date: 2011-09-14 11:36+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Slovenian <sl@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: 2011-09-15 04:48+0000\n" +"X-Generator: Launchpad (build 13921)\n" + +#. module: base_crypt +#: sql_constraint:res.users:0 +msgid "You can not have two users with the same login !" +msgstr "Ne morete imeti dva uporabnika z istim prijavnim imenom!" + +#. module: base_crypt +#: model:ir.model,name:base_crypt.model_res_users +msgid "res.users" +msgstr "res.users" + +#. module: base_crypt +#: constraint:res.users:0 +msgid "The chosen company is not in the allowed companies for this user" +msgstr "Izbrano podjetje ni v dovoljenih podjetjih za tega uporabnika" + +#. module: base_crypt +#: code:addons/base_crypt/crypt.py:132 +#, python-format +msgid "Please specify the password !" +msgstr "Prosim, navedite geslo !" + +#. module: base_crypt +#: model:ir.module.module,shortdesc:base_crypt.module_meta_information +msgid "Base - Password Encryption" +msgstr "Osnovno - enkripcija gesla" + +#. module: base_crypt +#: code:addons/base_crypt/crypt.py:132 +#, python-format +msgid "Error" +msgstr "Napaka" + +#. module: base_crypt +#: model:ir.module.module,description:base_crypt.module_meta_information +msgid "" +"This module replaces the cleartext password in the database with a password " +"hash,\n" +"preventing anyone from reading the original password.\n" +"For your existing user base, the removal of the cleartext passwords occurs " +"the first time\n" +"a user logs into the database, after installing base_crypt.\n" +"After installing this module it won't be possible to recover a forgotten " +"password for your\n" +"users, the only solution is for an admin to set a new password.\n" +"\n" +"Note: installing this module does not mean you can ignore basic security " +"measures,\n" +"as the password is still transmitted unencrypted on the network (by the " +"client),\n" +"unless you are using a secure protocol such as XML-RPCS.\n" +" " +msgstr "" +"Ta modul nadomeÅ¡Äa obliko besedilnega geslo v bazi podatkov z hash geslom,\n" +"ki prepreÄuje komurkoli branje izvirnega gesla.\n" +"Za vaÅ¡o obstojeÄo bazo uporabnikov, odstranitev gesla v obliki besedila se " +"pojavi pri\n" +"prvi prijavi uporabnika v bazo podatkov, po namestitvi base_crypt.\n" +"Po namestitvi tega modula ne bo mogoÄe obnoviti pozabiljenega gesla za vaÅ¡e\n" +"uporabnike, edina reÅ¡itev je, da admin nastavi novo geslo.\n" +"\n" +"Opomba: namestitev tega modula ne pomeni ignoriranja osnovnih varnostnih " +"ukrepov,\n" +"ker se geslo Å¡e vedno prenaÅ¡a neÅ¡ifrirano v omrežju (preko klienta),\n" +"razen Äe ne uporabljate varni protokol kot je XML-RPCS.\n" +" " diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py index 6393c0a61829d9dca55009338cf1d541cfc86590..58e5670b589fa214c20e9763a7886c629072bd59 100644 --- a/addons/base_vat/base_vat.py +++ b/addons/base_vat/base_vat.py @@ -42,7 +42,7 @@ _ref_vat = { 'pt': 'PT123456789', 'ro': 'RO1234567897', 'se': 'SE123456789701', 'si': 'SI12345679', 'sk': 'SK0012345675', 'el': 'EL12345670', - 'mx': 'MXABCD831230T1B', + 'mx': 'MXABC123456T1B', 'no': 'NO123456785' } @@ -1104,7 +1104,33 @@ class res_partner(osv.osv): #Valid format and valid date return True - + + + # check_vat_no contributed by Rolv RÃ¥en (adEgo) + def check_vat_no(self, vat): + ''' + Check Norway VAT number.See http://www.brreg.no/english/coordination/number.html + ''' + if len(vat) != 9: + return False + try: + int(vat) + except ValueError: + return False + + sum = (3 * int(vat[0])) + (2 * int(vat[1])) + \ + (7 * int(vat[2])) + (6 * int(vat[3])) + \ + (5 * int(vat[4])) + (4 * int(vat[5])) + \ + (3 * int(vat[6])) + (2 * int(vat[7])) + + check = 11 -(sum % 11) + if check == 11: + check = 0 + if check == 10: + # 10 is not a valid check digit for an organization number + return False + return check == int(vat[8]) + res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index 5e812207921cd5aff1b38a5cf0f472da29f7d404..c2dd5027b71d248aff37d7014d499ea1c9819f97 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -59,7 +59,7 @@ Creates a dashboard for CRM that includes: 'base_action_rule', 'base_setup', 'process', - 'mail_gateway', + 'mail', 'base_calendar', 'resource', 'board' @@ -85,7 +85,6 @@ Creates a dashboard for CRM that includes: 'wizard/crm_opportunity_to_phonecall_view.xml', 'wizard/crm_partner_to_opportunity_view.xml', - 'wizard/crm_send_email_view.xml', 'wizard/crm_add_note_view.xml', 'wizard/crm_merge_opportunities_view.xml', diff --git a/addons/crm/crm.py b/addons/crm/crm.py index d745e2859861a88bc17f495d154d532149aba182..c7bc461fa1f60ab0d7a139873ba0f0e7eb2cedc4 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -116,39 +116,12 @@ class crm_case_section(osv.osv): ('code_uniq', 'unique (code)', 'The code of the sales team must be unique !') ] - def _check_recursion(self, cr, uid, ids, context=None): - - """ - Checks for recursion level for sales team - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of Sales team ids - """ - level = 100 - - while len(ids): - cr.execute('select distinct parent_id from crm_case_section where id IN %s', (tuple(ids),)) - ids = filter(None, map(lambda x: x[0], cr.fetchall())) - if not level: - return False - level -= 1 - - return True - _constraints = [ - (_check_recursion, 'Error ! You cannot create recursive Sales team.', ['parent_id']) + (osv.osv._check_recursion, 'Error ! You cannot create recursive Sales team.', ['parent_id']) ] def name_get(self, cr, uid, ids, context=None): - """Overrides orm name_get method - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of sales team ids - """ - if context is None: - context = {} + """Overrides orm name_get method""" if not isinstance(ids, list) : ids = [ids] res = [] @@ -174,13 +147,7 @@ class crm_case_categ(osv.osv): } def _find_object_id(self, cr, uid, context=None): - """Finds id for case object - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param context: A standard dictionary for contextual values - """ - + """Finds id for case object""" object_id = context and context.get('object_id', False) or False ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)]) return ids and ids[0] @@ -217,6 +184,9 @@ class crm_base(object): if not context.get('portal'): return False # was user.address_id.id, but address_id has been removed + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if hasattr(user, 'partner_address_id') and user.partner_address_id: + return user.partner_address_id return False def _get_default_partner(self, cr, uid, context=None): @@ -228,6 +198,8 @@ class crm_base(object): if not context.get('portal', False): return False user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if hasattr(user, 'partner_address_id') and user.partner_address_id: + return user.partner_address_id return user.company_id.partner_id.id def _get_default_email(self, cr, uid, context=None): @@ -255,9 +227,9 @@ class crm_base(object): def onchange_partner_address_id(self, cr, uid, ids, add, email=False): """This function returns value of partner email based on Partner Address - @param ids: List of case IDs - @param add: Id of Partner's address - @email: Partner's email ID + :param ids: List of case IDs + :param add: Id of Partner's address + :param email: Partner's email ID """ if not add: return {'value': {'email_from': False}} @@ -269,9 +241,9 @@ class crm_base(object): def onchange_partner_id(self, cr, uid, ids, part, email=False): """This function returns value of partner address based on partner - @param ids: List of case IDs - @param part: Partner's id - @email: Partner's email ID + :param ids: List of case IDs + :param part: Partner's id + :param email: Partner's email ID """ data={} if part: @@ -282,6 +254,7 @@ class crm_base(object): def case_open(self, cr, uid, ids, *args): """Opens Case + :param ids: List of case Ids """ cases = self.browse(cr, uid, ids) for case in cases: @@ -295,7 +268,7 @@ class crm_base(object): def case_close(self, cr, uid, ids, *args): """Closes Case - @param ids: List of case Ids + :param ids: List of case Ids """ cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache @@ -306,7 +279,7 @@ class crm_base(object): def case_cancel(self, cr, uid, ids, *args): """Cancels Case - @param ids: List of case Ids + :param ids: List of case Ids """ cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache @@ -317,6 +290,7 @@ class crm_base(object): def case_pending(self, cr, uid, ids, *args): """Marks case as pending + :param ids: List of case Ids """ cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache @@ -326,13 +300,14 @@ class crm_base(object): def case_reset(self, cr, uid, ids, *args): """Resets case as draft + :param ids: List of case Ids """ cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache self.write(cr, uid, ids, {'state': 'draft', 'active': True}) self._action(cr, uid, cases, 'draft') return True - + def _action(self, cr, uid, cases, state_to, scrit=None, context=None): if context is None: context = {} @@ -394,10 +369,9 @@ class crm_case(crm_base): return self.stage_change(cr, uid, ids, '<', 'sequence desc', context) def copy(self, cr, uid, id, default=None, context=None): - """ Overrides orm copy method. - """ - if context is None: - context = {} + """Overrides orm copy method to avoid copying messages, + as well as date_closed and date_open columns if they + exist.""" if default is None: default = {} @@ -409,19 +383,11 @@ class crm_case(crm_base): default.update({ 'date_open': False }) return super(osv.osv, self).copy(cr, uid, id, default, context=context) - def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=[], context=None): - mailgate_pool = self.pool.get('mailgate.thread') - return mailgate_pool.history(cr, uid, cases, keyword, history=history,\ - subject=subject, email=email, \ - details=details, email_from=email_from,\ - message_id=message_id, attach=attach, \ - context=context) def case_open(self, cr, uid, ids, *args): - """Opens Case - """ + """Opens Case""" cases = self.browse(cr, uid, ids) - self._history(cr, uid, cases, _('Open')) + self.message_append(cr, uid, cases, _('Open')) for case in cases: data = {'state': 'open', 'active': True } if not case.user_id: @@ -431,11 +397,10 @@ class crm_case(crm_base): return True def case_close(self, cr, uid, ids, *args): - """Closes Case - """ + """Closes Case""" cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache - self._history(cr, uid, cases, _('Close')) + self.message_append(cr, uid, cases, _('Close')) self.write(cr, uid, ids, {'state': 'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'), }) @@ -446,12 +411,10 @@ class crm_case(crm_base): return True def case_escalate(self, cr, uid, ids, *args): - """Escalates case to top level - """ + """Escalates case to parent level""" cases = self.browse(cr, uid, ids) for case in cases: data = {'active': True} - if case.section_id.parent_id: data['section_id'] = case.section_id.parent_id.id if case.section_id.parent_id.change_responsible: @@ -461,16 +424,15 @@ class crm_case(crm_base): raise osv.except_osv(_('Error !'), _('You can not escalate, You are already at the top level regarding your sales-team category.')) self.write(cr, uid, [case.id], data) cases = self.browse(cr, uid, ids) - self._history(cr, uid, cases, _('Escalate')) + self.message_append(cr, uid, cases, _('Escalate')) self._action(cr, uid, cases, 'escalate') return True def case_cancel(self, cr, uid, ids, *args): - """Cancels Case - """ + """Cancels Case""" cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache - self._history(cr, uid, cases, _('Cancel')) + self.message_append(cr, uid, cases, _('Cancel')) self.write(cr, uid, ids, {'state': 'cancel', 'active': True}) self._action(cr, uid, cases, 'cancel') @@ -480,56 +442,37 @@ class crm_case(crm_base): return True def case_pending(self, cr, uid, ids, *args): - """Marks case as pending - """ + """Marks case as pending""" cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache - self._history(cr, uid, cases, _('Pending')) + self.message_append(cr, uid, cases, _('Pending')) self.write(cr, uid, ids, {'state': 'pending', 'active': True}) self._action(cr, uid, cases, 'pending') return True def case_reset(self, cr, uid, ids, *args): - """Resets case as draft - """ - state = 'draft' + """Resets case as draft""" + state = 'draft' if 'crm.phonecall' in args: - state = 'open' + state = 'open' cases = self.browse(cr, uid, ids) cases[0].state # to fill the browse record cache - self._history(cr, uid, cases, _('Draft')) + self.message_append(cr, uid, cases, _('Draft')) self.write(cr, uid, ids, {'state': state, 'active': True}) self._action(cr, uid, cases, state) return True def remind_partner(self, cr, uid, ids, context=None, attach=False): - - """ - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of Remind Partner's IDs - @param context: A standard dictionary for contextual values - - """ - return self.remind_user(cr, uid, ids, context, attach, destination=False) def remind_user(self, cr, uid, ids, context=None, attach=False, destination=True): - """ - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of case's IDs to remind - @param context: A standard dictionary for contextual values - """ + mail_message = self.pool.get('mail.message') for case in self.browse(cr, uid, ids, context=context): if not destination and not case.email_from: return False if not case.user_id.user_email: return False - if destination and case.section_id.user_id: case_email = case.section_id.user_id.user_email else: @@ -550,37 +493,31 @@ class crm_case(crm_base): body = self.format_body(body) - attach_to_send = None + attach_to_send = {} if attach: attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)]) attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname', 'datas']) - attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send) + attach_to_send = dict(map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send)) - # Send an email - subject = "Reminder: [%s] %s" % (str(case.id), case.name,) - tools.email_send( + # Send an email + subject = "Reminder: [%s] %s" % (str(case.id), case.name, ) + mail_message.schedule_with_attach(cr, uid, src, [dest], subject, body, - reply_to=case.section_id.reply_to or '', - openobject_id=str(case.id), - attach=attach_to_send + model='crm.case', + reply_to=case.section_id.reply_to, + res_id=case.id, + attachments=attach_to_send, + context=context ) - self._history(cr, uid, [case], _('Send'), history=True, subject=subject, email=dest, details=body, email_from=src) - return True def _check(self, cr, uid, ids=False, context=None): - """ - Function called by the scheduler to process cases for date actions - Only works on not done and cancelled cases - - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param context: A standard dictionary for contextual values + """Function called by the scheduler to process cases for date actions + Only works on not done and cancelled cases """ cr.execute('select * from crm_case \ where (date_action_last<%s or date_action_last is null) \ @@ -599,9 +536,7 @@ class crm_case(crm_base): def format_mail(self, obj, body): return self.pool.get('base.action.rule').format_mail(obj, body) - def message_followers(self, cr, uid, ids, context=None): - """ Get a list of emails of the people following this thread - """ + def message_thread_followers(self, cr, uid, ids, context=None): res = {} for case in self.browse(cr, uid, ids, context=context): l=[] @@ -613,12 +548,7 @@ class crm_case(crm_base): return res def _links_get(self, cr, uid, context=None): - """Gets links value for reference field - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param context: A standard dictionary for contextual values - """ + """Gets links value for reference field""" obj = self.pool.get('res.request.link') ids = obj.search(cr, uid, []) res = obj.read(cr, uid, ids, ['object', 'name'], context) @@ -634,10 +564,8 @@ class users(osv.osv): def create(self, cr, uid, vals, context=None): res = super(users, self).create(cr, uid, vals, context=context) section_obj=self.pool.get('crm.case.section') - - if vals.get('context_section_id', False): + if vals.get('context_section_id'): section_obj.write(cr, uid, [vals['context_section_id']], {'member_ids':[(4, res)]}, context) return res users() - diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 31dcc0be56ace16812479092205342fa3fc6491a..559d257820aa4e56cb478b6c5f3d96fbf86052e3 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -40,12 +40,12 @@ class base_action_rule(osv.osv): 'regex_history' : fields.char('Regular Expression on Case History', size=128), 'act_section_id': fields.many2one('crm.case.section', 'Set Team to'), 'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'), - 'act_mail_to_partner': fields.boolean('Mail to Partner', help="Check \ -this if you want the rule to send an email to the partner."), + 'act_mail_to_partner': fields.boolean('Mail to Partner', + help="Check this if you want the rule to send an email to the partner."), } - def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from', False), context=None): + mail_message = self.pool.get('mail.message') body = self.format_mail(obj, body) if not emailfrom: if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.user_email: @@ -58,15 +58,10 @@ this if you want the rule to send an email to the partner."), else: reply_to = emailfrom if not emailfrom: - raise osv.except_osv(_('Error!'), - _("No E-Mail ID Found for your Company address!")) - return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id)) + raise osv.except_osv(_('Error!'), _("No E-Mail Found for your Company address!")) + return mail_message.schedule_with_attach(cr, uid, emailfrom, emails, name, body, model='base.action.rule', reply_to=reply_to, res_id=obj.id) def do_check(self, cr, uid, action, obj, context=None): - """ @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param context: A standard dictionary for contextual values""" ok = super(base_action_rule, self).do_check(cr, uid, action, obj, context=context) if hasattr(obj, 'section_id'): @@ -74,9 +69,8 @@ this if you want the rule to send an email to the partner."), if hasattr(obj, 'categ_id'): ok = ok and (not action.trg_categ_id or action.trg_categ_id.id == obj.categ_id.id) - #Cheking for history + #Cheking for history regex = action.regex_history - result_history = True if regex: res = False ptrn = re.compile(str(regex)) @@ -85,23 +79,17 @@ this if you want the rule to send an email to the partner."), if _result: res = True break - result_history = res - ok = ok and (not regex or result_history) + ok = ok and res - res_count = True if action.trg_max_history: res_count = False - history_ids = filter(lambda x: x.history, obj.message_ids) + history_ids = filter(lambda x: x.email_from, obj.message_ids) if len(history_ids) <= action.trg_max_history: res_count = True - ok = ok and res_count + ok = ok and res_count return ok def do_action(self, cr, uid, action, model_obj, obj, context=None): - """ @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param context: A standard dictionary for contextual values """ res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context) write = {} @@ -118,8 +106,8 @@ this if you want the rule to send an email to the partner."), write['email_cc'] = obj.act_email_cc # Put state change by rule in communication history - if hasattr(obj, 'state') and action.act_state: - model_obj._history(cr, uid, [obj], _(action.act_state)) + if hasattr(obj, 'state') and hasattr(obj, 'message_append') and action.act_state: + model_obj.message_append(cr, uid, [obj], _(action.act_state)) model_obj.write(cr, uid, [obj.id], write, context) emails = [] @@ -134,22 +122,12 @@ this if you want the rule to send an email to the partner."), def state_get(self, cr, uid, context=None): - """Gets available states for crm - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param context: A standard dictionary for contextual values """ + """Gets available states for crm""" res = super(base_action_rule, self).state_get(cr, uid, context=context) return res + crm.AVAILABLE_STATES def priority_get(self, cr, uid, context=None): - """@param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param context: A standard dictionary for contextual values """ res = super(base_action_rule, self).priority_get(cr, uid, context=context) return res + crm.AVAILABLE_PRIORITIES -base_action_rule() - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index fae37ed54a420eb22919d31c59804cc3449ac4a6..d09e0bf8ba572ad8d8eb69f37ef620eb712ae043 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -40,7 +40,15 @@ class crm_lead(crm_case, osv.osv): _name = "crm.lead" _description = "Lead/Opportunity" _order = "priority,date_action,id desc" - _inherit = ['mailgate.thread','res.partner.address'] + _inherit = ['mail.thread','res.partner.address'] + + # overridden because res.partner.address has an inconvenient name_get, + # especially if base_contact is installed. + def name_get(self, cr, user, ids, context=None): + if isinstance(ids, (int, long)): + ids = [ids] + return [(r['id'], tools.ustr(r[self._rec_name])) + for r in self.read(cr, user, ids, [self._rec_name], context)] def _compute_day(self, cr, uid, ids, fields, args, context=None): """ @@ -101,8 +109,8 @@ class crm_lead(crm_case, osv.osv): def _history_search(self, cr, uid, obj, name, args, context=None): res = [] - msg_obj = self.pool.get('mailgate.message') - message_ids = msg_obj.search(cr, uid, [('history','=',True), ('name', args[0][1], args[0][2])], context=context) + msg_obj = self.pool.get('mail.message') + message_ids = msg_obj.search(cr, uid, [('email_from','!=',False), ('subject', args[0][1], args[0][2])], context=context) lead_ids = self.search(cr, uid, [('message_ids', 'in', message_ids)], context=context) if lead_ids: @@ -115,8 +123,8 @@ class crm_lead(crm_case, osv.osv): for obj in self.browse(cr, uid, ids, context=context): res[obj.id] = '' for msg in obj.message_ids: - if msg.history: - res[obj.id] = msg.name + if msg.email_from: + res[obj.id] = msg.subject break return res @@ -163,7 +171,7 @@ class crm_lead(crm_case, osv.osv): \nIf the case is in progress the state is set to \'Open\'.\ \nWhen the case is over, the state is set to \'Done\'.\ \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), 'subjects': fields.function(_get_email_subject, fnct_search=_history_search, string='Subject of Email', type='char', size=64), @@ -178,6 +186,12 @@ class crm_lead(crm_case, osv.osv): 'date_action': fields.date('Next Action Date'), 'title_action': fields.char('Next Action', size=64), 'stage_id': fields.many2one('crm.case.stage', 'Stage', domain="[('section_ids', '=', section_id)]"), + 'color': fields.integer('Color Index'), + 'partner_address_name': fields.related('partner_address_id', 'name', type='char', string='Partner Contact Name', readonly=True), + 'company_currency': fields.related('company_id', 'currency_id', 'symbol', type='char', string='Company Currency', readonly=True), + 'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True), + 'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True), + } _defaults = { @@ -189,6 +203,7 @@ class crm_lead(crm_case, osv.osv): 'section_id': crm_case._get_section, 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c), 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], + 'color': 0, #'stage_id': _get_stage_id, } @@ -304,6 +319,21 @@ class crm_lead(crm_case, osv.osv): self.log(cr, uid, l.id, message) return res + def set_priority(self, cr, uid, ids, priority): + """Set lead priority + """ + return self.write(cr, uid, ids, {'priority' : priority}) + + def set_high_priority(self, cr, uid, ids, *args): + """Set lead priority to high + """ + return self.set_priority(cr, uid, ids, '1') + + def set_normal_priority(self, cr, uid, ids, *args): + """Set lead priority to normal + """ + return self.set_priority(cr, uid, ids, '3') + def convert_opportunity(self, cr, uid, ids, context=None): """ Precomputation for converting lead to opportunity """ @@ -335,16 +365,14 @@ class crm_lead(crm_case, osv.osv): } return value - def message_new(self, cr, uid, msg, context=None): - """ Automatically calls when new email message arrives - """ - mailgate_pool = self.pool.get('email.server.tools') + def message_new(self, cr, uid, msg, custom_values=None, context=None): + """Automatically calls when new email message arrives""" + res_id = super(crm_lead, self).message_new(cr, uid, msg, custom_values=custom_values, context=context) + subject = msg.get('subject') or _("No Subject") + body = msg.get('body_text') - subject = msg.get('subject') or _("No Subject") - body = msg.get('body') msg_from = msg.get('from') priority = msg.get('priority') - vals = { 'name': subject, 'email_from': msg_from, @@ -352,45 +380,27 @@ class crm_lead(crm_case, osv.osv): 'description': body, 'user_id': False, } - if msg.get('priority', False): + if priority: vals['priority'] = priority + vals.update(self.message_partner_by_email(cr, uid, msg.get('from', False))) + self.write(cr, uid, [res_id], vals, context) + return res_id - res = mailgate_pool.get_partner(cr, uid, msg.get('from') or msg.get_unixfrom()) - if res: - vals.update(res) - - res = self.create(cr, uid, vals, context) - attachents = msg.get('attachments', []) - for attactment in attachents or []: - data_attach = { - 'name': attactment, - 'datas':binascii.b2a_base64(str(attachents.get(attactment))), - 'datas_fname': attactment, - 'description': 'Mail attachment', - 'res_model': self._name, - 'res_id': res, - } - self.pool.get('ir.attachment').create(cr, uid, data_attach) - - return res - - def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context=None): - """ - @param ids: List of update mail’s IDs - """ + def message_update(self, cr, uid, ids, msg, vals={}, default_act='pending', context=None): if isinstance(ids, (str, int, long)): ids = [ids] + super(crm_lead, self).message_update(cr, uid, ids, msg, context=context) + if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): vals['priority'] = msg.get('priority') - maps = { 'cost':'planned_cost', 'revenue': 'planned_revenue', 'probability':'probability' } vls = {} - for line in msg['body'].split('\n'): + for line in msg['body_text'].split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower()): @@ -408,12 +418,6 @@ class crm_lead(crm_case, osv.osv): res = self.write(cr, uid, [case.id], values, context=context) return res - def msg_send(self, cr, uid, id, *args, **argv): - """ Send The Message - @param ids: List of email’s IDs - """ - return True - def action_makeMeeting(self, cr, uid, ids, context=None): """ This opens Meeting's calendar view to schedule meeting on current Opportunity @@ -459,6 +463,15 @@ class crm_lead(crm_case, osv.osv): } return value + + 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(_('Warning !'), + _('You can not delete this lead. You should better cancel it.')) + return super(crm_lead, self).unlink(cr, uid, ids, context) + + def write(self, cr, uid, ids, vals, context=None): if not context: context = {} @@ -468,7 +481,8 @@ class crm_lead(crm_case, osv.osv): if 'stage_id' in vals and vals['stage_id']: stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context) - self.history(cr, uid, ids, _("Changed Stage to: %s") % stage_obj.name, details=_("Changed Stage to: %s") % stage_obj.name) + text = _("Changed Stage to: %s") % stage_obj.name + self.message_append(cr, uid, ids, text, body_text=text, context=context) message='' for case in self.browse(cr, uid, ids, context=context): if case.type == 'lead' or context.get('stage_type',False)=='lead': @@ -478,14 +492,6 @@ class crm_lead(crm_case, osv.osv): self.log(cr, uid, case.id, message) return super(crm_lead,self).write(cr, uid, ids, vals, context) - 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(_('Warning !'), - _('You can not delete this lead. You should better cancel it.')) - return super(crm_lead, self).unlink(cr, uid, ids, context) - - crm_lead() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 0ad576fd470404c9f22cb86caf8f13c3158933a8..8811cea8ac4169da484350c82caf56421dc10650 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -97,10 +97,10 @@ </group> <group colspan="2" col="3"> <separator string="Communication" colspan="4" col="3"/> - <field name="email_from" widget="email"/><button string="Mail" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'crm.lead'}" - icon="terp-mail-message-new" type="action" colspan="1"/> + <field name="email_from" widget="email"/> + <button string="Mail" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action" colspan="1"/> <newline/> <field name="phone"/> <newline/> @@ -174,52 +174,53 @@ <group colspan="4"> <field colspan="4" name="email_cc" widget="char" size="512"/> </group> - <field name="message_ids" colspan="4" nolabel="1" mode="tree,form"> + <field name="message_ids" colspan="4" nolabel="1" mode="tree,form" readonly="1"> <tree string="History"> <field name="display_text" string="History Information"/> - <field name="history" invisible="1"/> + <field name="email_from" invisible="1"/> <button - string="Reply" attrs="{'invisible': [('history', '!=', True)]}" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}" + string="Reply" attrs="{'invisible': [('email_from', '=', False)]}" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" icon="terp-mail-replied" type="action" /> </tree> - <form string="History"> - <group col="4" colspan="4"> + <form string="History"> + <group col="4" colspan="4"> + <group col="2" colspan="2" attrs="{'invisible': [('email_from', '=', False)]}"> <field name="email_from"/> - <field name="date"/> <field name="email_to" size="512"/> - <field name="email_cc" size="512"/> - <field name="name" colspan="4" widget="char" attrs="{'invisible': [('history', '=', False)]}" size="512"/> - <field name="display_text" colspan="4" attrs="{'invisible': [('history', '=', True)]}"/> - <field name="history" invisible="1"/> </group> - <notebook colspan="4"> - <page string="Details"> - <field name="description" colspan="4" nolabel="1"/> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <button colspan="4" - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> - </group> - - </page> - <page string="Attachments"> - <field name="attachment_ids" colspan="4" readonly="1" nolabel="1"/> - </page> - </notebook> - </form> + <group col="2" colspan="2"> + <field name="date"/> + <field name="email_cc" size="512" attrs="{'invisible': [('email_from', '=', False)]}"/> + </group> + <field name="subject" colspan="4" widget="char" attrs="{'invisible': [('email_from', '=', False)]}" size="512"/> + <field name="display_text" colspan="4" attrs="{'invisible': [('email_from', '!=', False)]}"/> + </group> + <notebook colspan="4"> + <page string="Details" attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1"/> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <button colspan="4" string="Reply" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply'}" + icon="terp-mail-replied" type="action"/> + </group> + + </page> + <page string="Attachments" attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="attachment_ids" colspan="4" readonly="1" nolabel="1"/> + </page> + </notebook> + </form> </field> <button string="Add Internal Note" name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action" /> <button string="Send New Email" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'crm.lead'}" - icon="terp-mail-message-new" type="action" /> + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action"/> </page> </notebook> </form> @@ -297,60 +298,63 @@ <field name="model">crm.lead</field> <field name="type">kanban</field> <field name="arch" type="xml"> - <kanban> + <kanban default_group_by="stage_id"> <field name="state"/> + <field name="color"/> + <field name="priority"/> + <field name="planned_revenue"/> + <field name="user_email"/> <templates> <t t-name="kanban-box"> - <div t-attf-class="oe_kanban_box oe_kanban_color_border #{__kanban_color(state.raw_value)}"> - <div class="oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle"> - <h6 class="oe_kanban_left"><field name="name"/></h6> - <h5 class="oe_kanban_right" style="vertical-align: top"> - <field name="planned_revenue"/> - <img src="http://www.gravatar.com/avatar/2eb60ad22dadcf4dc456b28390a80268.png" style="border-radius: 2px" width="20" height="20"/> - </h5> - <br class="oe_kanban_clear"/> - </div> - - <div class="oe_kanban_box_content oe_kanban_color_bglight"> - <div class="oe_kanban_right oe_kanban_small"> - <field name="user_id"/> + <t t-set="color" t-value="kanban_color(record.color.raw_value || record.state.raw_value)"/> + <div t-att-class="color + (record.priority.raw_value == 1 ? ' oe_kanban_color_alert' : '')"> + <div class="oe_kanban_box oe_kanban_color_border"> + <table class="oe_kanban_table oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle"> + <tr> + <td class="oe_kanban_title3" align="left" valign="middle"> + <a t-if="record.priority.raw_value == 1" icon="star-on" type="object" name="set_normal_priority"/> + <a t-if="record.priority.raw_value != 1" icon="star-off" type="object" name="set_high_priority" style="opacity:0.6; filter:alpha(opacity=60);"/> + <field name="partner_id"/> + </td> + <td class="oe_kanban_title2" align="right" valign="middle" t-if="record.planned_revenue.raw_value" nowrap="nowrap"> + <t t-esc="Math.round(record.planned_revenue.value)"/> <field name="company_currency"/> + </td> + <td valign="top" width="22"><img t-att-src="kanban_gravatar(record.user_email.value, 22)" class="oe_kanban_gravatar"/></td> + </tr> + </table> + + <div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger"> + <div class="oe_kanban_right oe_kanban_small"> + <field name="user_login"/> + </div> + <div> + <b><field name="partner_address_name"/></b> + </div> + <div> + <field name="name"/> + </div> + <div style="padding-left: 0.5em"> + <i><field name="date_action"/><t t-if="record.date_action.raw_value"> : </t><field name="title_action"/></i> + </div> </div> - <br class="oe_kanban_clear"/> - - <b><field name="partner_address_id"/></b><br/> - <field name="description"/><br/> - <field name="date_action"/><t t-if="date_action.raw_value">: </t><field name="title_action"/> - </div> - <div class="oe_kanban_buttons_set oe_kanban_color_border oe_kanban_color_bglight oe_kanban_box_show_onclick"> - <div class="oe_kanban_left"> - <a string="Send New Email" class="oe_kanban_color_border" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'crm.lead'}" - icon="terp-mail-message-new" type="action"/> - - <a string="Schedule/Log Call" class="oe_kanban_color_border" - name="%(opportunity2phonecall_act)d" - icon="terp-call-start" type="action"/> - - <a string="Add Internal Note" class="oe_kanban_color_border" - name="%(crm.action_crm_add_note)d" - context="{'model': 'crm.lead' }" - icon="terp-document-new" type="action"/> - - <a name="action_makeMeeting" type="object" class="oe_kanban_color_border" - string="Schedule Meeting" icon="gtk-redo" /> + <div class="oe_kanban_buttons_set oe_kanban_color_border oe_kanban_color_bglight oe_kanban_box_show_onclick"> + <div class="oe_kanban_left"> + <a string="Edit" icon="gtk-edit" type="edit"/> + <a string="Delete" icon="gtk-close" type="delete"/> + <a string="Change Color" icon="color-picker" type="color" name="color"/> + <a string="Send New Email" name="%(mail.action_email_compose_message_wizard)d" icon="terp-mail-message-new" type="action"/> + <a string="Schedule/Log Call" name="%(opportunity2phonecall_act)d" icon="terp-call-start" type="action"/> + <a string="Add Internal Note" name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action"/> + <a name="action_makeMeeting" type="object" string="Schedule Meeting" icon="gtk-redo" /> + </div> + <div class="oe_kanban_right"> + <a name="case_pending" string="Pending" states="draft,open" type="object" icon="lead-stage-pending" /> + <a name="case_mark_won" string="Mark Won" states="open,pending" type="object" icon="lead-stage-won" /> + <a name="case_mark_lost" string="Mark Lost" states="open,pending" type="object" icon="lead-stage-lost" /> + </div> + <br class="oe_kanban_clear"/> </div> - <div class="oe_kanban_right"> - <a name="case_cancel" string="Cancel" states="draft" type="object" icon="gtk-cancel" /> - <a name="case_mark_lost" string="Mark Lost" states="open,pending" type="object" icon="gtk-cancel" /> - <a name="case_reset" string="Reset to Draft" states="done,cancel" type="object" icon="gtk-convert" /> - <a name="case_open" string="Open" states="draft,pending" type="object" icon="gtk-go-forward" /> - <a name="case_pending" string="Pending" states="draft,open" type="object" icon="gtk-media-pause" /> - <a name="case_escalate" string="Escalate" states="open,pending" type="object" icon="gtk-go-up" /> - <a name="case_mark_won" string="Mark Won" states="open,pending" type="object" icon="gtk-apply" /> - </div> - <br class="oe_kanban_clear"/> </div> </div> </t> @@ -492,7 +496,7 @@ <group col="3" colspan="2"> <field name="email_from" string="Email" /> <button string="Mail" - name="%(crm.action_crm_send_mail)d" + name="%(mail.action_email_compose_message_wizard)d" context="{'mail':'new', 'model': 'crm.lead'}" icon="terp-mail-message-new" type="action" /> </group> @@ -552,58 +556,56 @@ <field name="optout" on_change="on_change_optout(optout)"/> </group> </page> - <page string="Communication & History" groups="base.group_extended"> - <group colspan="4"> - <field colspan="4" name="email_cc" string="Global CC" widget="char" size="512"/> - </group> - <field name="message_ids" colspan="4" nolabel="1" mode="tree,form"> - <tree string="History"> - <field name="display_text" string="History Information"/> - <field name="history" invisible="1"/> - <button - string="Reply" attrs="{'invisible': [('history', '!=', True)]}" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> - </tree> - <form string="History"> - <group col="4" colspan="4"> - <field name="email_from"/> - <field name="date"/> - <field name="email_to" size="512"/> - <field name="email_cc" size="512"/> - <field name="name" colspan="4" widget="char" attrs="{'invisible': [('history', '=', False)]}" size="512"/> - <field name="display_text" colspan="4" attrs="{'invisible': [('history', '=', True)]}"/> - <field name="history" invisible="1"/> - </group> - <notebook colspan="4"> - <page string="Details"> - - <field name="description" colspan="4" nolabel="1"/> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <button colspan="4" - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> - </group> - - </page> - <page string="Attachments"> - <field name="attachment_ids" colspan="4" readonly="1" nolabel="1"/> + <group colspan="4"> + <field colspan="4" name="email_cc" widget="char" size="512"/> + </group> + <field name="message_ids" colspan="4" nolabel="1" mode="tree,form" readonly="1"> + <tree string="History"> + <field name="display_text" string="History Information"/> + <field name="email_from" invisible="1"/> + <button + string="Reply" attrs="{'invisible': [('email_from', '=', False)]}" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action" /> + </tree> + <form string="History"> + <group col="4" colspan="4"> + <group col="2" colspan="2" attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="email_from"/> + <field name="email_to" size="512"/> + </group> + <group col="2" colspan="2"> + <field name="date"/> + <field name="email_cc" size="512" attrs="{'invisible': [('email_from', '=', False)]}"/> + </group> + <field name="subject" colspan="4" widget="char" attrs="{'invisible': [('email_from', '=', False)]}" size="512"/> + <field name="display_text" colspan="4" attrs="{'invisible': [('email_from', '!=', False)]}"/> + </group> + <notebook colspan="4"> + <page string="Details" attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1"/> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <button colspan="4" string="Reply" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply'}" + icon="terp-mail-replied" type="action"/> + </group> </page> - </notebook> - </form> - </field> - <button string="Add Internal Note" - name="%(crm.action_crm_add_note)d" - context="{'model': 'crm.lead' }" - icon="terp-document-new" type="action" /> - <button string="Send New Email" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'crm.lead'}" - icon="terp-mail-message-new" type="action" /> + <page string="Attachments" attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="attachment_ids" colspan="4" readonly="1" nolabel="1"/> + </page> + </notebook> + </form> + </field> + <button string="Add Internal Note" + name="%(crm.action_crm_add_note)d" + context="{'model': 'crm.lead' }" + icon="terp-document-new" type="action" /> + <button string="Send New Email" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action"/> </page> <page string="Extra Info" groups="base.group_extended"> <group col="2" colspan="2"> diff --git a/addons/crm/crm_meeting.py b/addons/crm/crm_meeting.py index 4e329ddc6d4f1a5bb23e8a56f4bb664c0610c36b..c3381601cbfb8d10698682154774cf4adbffc001 100644 --- a/addons/crm/crm_meeting.py +++ b/addons/crm/crm_meeting.py @@ -45,12 +45,12 @@ class crm_meeting(crm_base, osv.osv): _inherit = "calendar.event" _columns = { # From crm.case - 'name': fields.char('Summary', size=124, required=True, states={'done': [('readonly', True)]}), - 'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}), + 'name': fields.char('Summary', size=124, required=True, states={'done': [('readonly', True)]}), + 'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \ - domain="[('partner_id','=',partner_id)]", states={'done': [('readonly', True)]}), + domain="[('partner_id','=',partner_id)]", states={'done': [('readonly', True)]}), 'section_id': fields.many2one('crm.case.section', 'Sales Team', states={'done': [('readonly', True)]}, \ - select=True, help='Sales team to which Case belongs to.'), + select=True, help='Sales team to which Case belongs to.'), 'email_from': fields.char('Email', size=128, states={'done': [('readonly', True)]}, help="These people will receive email."), 'id': fields.integer('ID'), 'create_date': fields.datetime('Creation Date' , readonly=True), @@ -67,7 +67,7 @@ class crm_meeting(crm_base, osv.osv): 'event_id', 'attendee_id', 'Attendees', states={'done': [('readonly', True)]}), 'date_closed': fields.datetime('Closed', readonly=True), 'date_deadline': fields.datetime('Deadline', states={'done': [('readonly', True)]}), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), 'state': fields.selection([('open', 'Confirmed'), ('draft', 'Unconfirmed'), ('cancel', 'Cancelled'), @@ -75,7 +75,7 @@ class crm_meeting(crm_base, osv.osv): size=16, readonly=True), } _defaults = { - 'state': 'draft', + 'state': 'draft', 'active': 1, 'user_id': lambda self, cr, uid, ctx: uid, } @@ -138,7 +138,7 @@ class res_users(osv.osv): def create(self, cr, uid, data, context=None): user_id = super(res_users, self).create(cr, uid, data, context=context) - + # add shortcut unless 'noshortcut' is True in context if not(context and context.get('noshortcut', False)): data_obj = self.pool.get('ir.model.data') @@ -150,7 +150,6 @@ class res_users(osv.osv): except: # Tolerate a missing shortcut. See product/product.py for similar code. logging.getLogger('orm').debug('Skipped meetings shortcut for user "%s"', data.get('name','<new')) - return user_id res_users() diff --git a/addons/crm/crm_phonecall.py b/addons/crm/crm_phonecall.py index 06c90417725ba98032540840b4672b41b831154f..d5d7d6c5614f241c757a5884c1cc092785db450f 100644 --- a/addons/crm/crm_phonecall.py +++ b/addons/crm/crm_phonecall.py @@ -36,18 +36,18 @@ class crm_phonecall(crm_base, osv.osv): # From crm.case 'id': fields.integer('ID'), 'name': fields.char('Call Summary', size=64), - 'active': fields.boolean('Active', required=False), + 'active': fields.boolean('Active', required=False), 'date_action_last': fields.datetime('Last Action', readonly=1), - 'date_action_next': fields.datetime('Next Action', readonly=1), + 'date_action_next': fields.datetime('Next Action', readonly=1), 'create_date': fields.datetime('Creation Date' , readonly=True), 'section_id': fields.many2one('crm.case.section', 'Sales Team', \ - select=True, help='Sales team to which Case belongs to.'), - 'user_id': fields.many2one('res.users', 'Responsible'), - 'partner_id': fields.many2one('res.partner', 'Partner'), + select=True, help='Sales team to which Case belongs to.'), + 'user_id': fields.many2one('res.users', 'Responsible'), + 'partner_id': fields.many2one('res.partner', 'Partner'), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \ - domain="[('partner_id','=',partner_id)]"), - 'company_id': fields.many2one('res.company', 'Company'), - 'description': fields.text('Description'), + domain="[('partner_id','=',partner_id)]"), + 'company_id': fields.many2one('res.company', 'Company'), + 'description': fields.text('Description'), 'state': fields.selection([ ('draft', 'Draft'), ('open', 'Todo'), @@ -57,24 +57,24 @@ class crm_phonecall(crm_base, osv.osv): ], 'State', size=16, readonly=True, help='The state is set to \'Todo\', when a case is created.\ \nIf the case is in progress the state is set to \'Open\'.\ - \nWhen the case is over, the state is set to \'Done\'.\ - \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), - 'email_from': fields.char('Email', size=128, help="These people will receive email."), + \nWhen the call is over, the state is set to \'Held\'.\ + \nIf the call needs to be done then the state is set to \'Not Held\'.'), + 'email_from': fields.char('Email', size=128, help="These people will receive email."), 'date_open': fields.datetime('Opened', readonly=True), # phonecall fields - 'duration': fields.float('Duration', help="Duration in Minutes"), + 'duration': fields.float('Duration', help="Duration in Minutes"), 'categ_id': fields.many2one('crm.case.categ', 'Category', \ domain="['|',('section_id','=',section_id),('section_id','=',False),\ - ('object_id.model', '=', 'crm.phonecall')]"), - 'partner_phone': fields.char('Phone', size=32), + ('object_id.model', '=', 'crm.phonecall')]"), + 'partner_phone': fields.char('Phone', size=32), 'partner_contact': fields.related('partner_address_id', 'name', \ - type="char", string="Contact", size=128), - 'partner_mobile': fields.char('Mobile', size=32), - 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), + type="char", string="Contact", size=128), + 'partner_mobile': fields.char('Mobile', size=32), + 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), 'date_closed': fields.datetime('Closed', readonly=True), 'date': fields.datetime('Date'), 'opportunity_id': fields.many2one ('crm.lead', 'Lead/Opportunity'), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), } def _get_default_state(self, cr, uid, context=None): @@ -83,17 +83,14 @@ class crm_phonecall(crm_base, osv.osv): return 'open' _defaults = { - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), 'priority': crm.AVAILABLE_PRIORITIES[2][0], 'state': _get_default_state, 'user_id': lambda self,cr,uid,ctx: uid, - 'active': 1, + 'active': 1, } - - - - # From crm.case + # From crm.case def onchange_partner_address_id(self, cr, uid, ids, add, email=False): res = super(crm_phonecall, self).onchange_partner_address_id(cr, uid, ids, add, email) res.setdefault('value', {}) @@ -176,23 +173,23 @@ class crm_phonecall(crm_base, osv.osv): id3 = data_obj.browse(cr, uid, id3, context=context).res_id context = { - 'default_phonecall_id': phonecall.id, - 'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False, - 'default_email': phonecall.email_from , + 'default_phonecall_id': phonecall.id, + 'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False, + 'default_email': phonecall.email_from , 'default_name': phonecall.name } value = { - 'name': _('Meetings'), - 'domain' : "[('user_id','=',%s)]" % (uid), - 'context': context, - 'view_type': 'form', - 'view_mode': 'calendar,form,tree', - 'res_model': 'crm.meeting', - 'view_id': False, - 'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')], - 'type': 'ir.actions.act_window', - 'search_view_id': res['res_id'], + 'name': _('Meetings'), + 'domain' : "[('user_id','=',%s)]" % (uid), + 'context': context, + 'view_type': 'form', + 'view_mode': 'calendar,form,tree', + 'res_model': 'crm.meeting', + 'view_id': False, + 'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')], + 'type': 'ir.actions.act_window', + 'search_view_id': res['res_id'], 'nodestroy': True } diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index d58348a56ddb94f458f7cb4f549105544a63a1fa..c7038958126e2b55f7b895416a6599de321e257e 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -74,7 +74,7 @@ <notebook colspan="4"> <page string="Sales Team"> <group col="2" colspan="1"> - <separator string="Mailgateway" colspan="2"/> + <separator string="Mail Gateway" colspan="2"/> <field name="reply_to" select="2"/> </group> <group col="2" colspan="1"> diff --git a/addons/crm/report/crm_lead_report.py b/addons/crm/report/crm_lead_report.py index bd61e1ee80da3000049f167fbfab3494979432be..18615bdf344cd22f6a3e6830858a7ca05108aa09 100644 --- a/addons/crm/report/crm_lead_report.py +++ b/addons/crm/report/crm_lead_report.py @@ -121,7 +121,7 @@ class crm_lead_report(osv.osv): c.planned_revenue, c.planned_revenue*(c.probability/100) as probable_revenue, 1 as nbr, - (SELECT count(id) FROM mailgate_message WHERE model='crm.lead' AND res_id=c.id AND history=True) AS email, + (SELECT count(id) FROM mail_message WHERE model='crm.lead' AND res_id=c.id AND email_from is not null) AS email, date_trunc('day',c.create_date) as create_date, extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close, abs(extract('epoch' from (c.date_deadline - c.date_closed))/(3600*24)) as delay_expected, diff --git a/addons/crm/security/ir.model.access.csv b/addons/crm/security/ir.model.access.csv index 6f7b3066d7ad0d61a33f1ea81140f9c861006235..6b0a3ac15a9a89940aace83884340ecfd42503dc 100644 --- a/addons/crm/security/ir.model.access.csv +++ b/addons/crm/security/ir.model.access.csv @@ -29,15 +29,15 @@ "access_res_partner_manager","res.partner.crm.manager","base.model_res_partner","base.group_sale_manager",1,0,0,0 "access_res_partner_address_manager","res.partner.address.crm.user.manager","base.model_res_partner_address","base.group_sale_manager",1,0,0,0 "access_res_partner_category_manager","res.partner.category.crm.manager","base.model_res_partner_category","base.group_sale_manager",1,0,0,0 -"mail_gateway_mailgate_message_manager","mail_gateway.mailgate.message.manager","mail_gateway.model_mailgate_message","base.group_sale_manager",1,0,0,0 -"mail_gateway_mailgate_thread_manager","mail_gateway.mailgate.thread.manager","mail_gateway.model_mailgate_thread","base.group_sale_manager",1,1,1,1 +"mail_mail_message_manager","mail.message.manager","mail.model_mail_message","base.group_sale_manager",1,0,0,0 +"mail_thread_manager","mail.thread.manager","mail.model_mail_thread","base.group_sale_manager",1,1,1,1 "access_calendar_attendee_crm_user","calendar.attendee.crm.user","model_calendar_attendee","base.group_sale_salesman",1,1,1,0 "access_calendar_attendee_crm_manager","calendar.attendee.crm.manager","model_calendar_attendee","base.group_sale_manager",1,1,1,1 "access_res_partner","res.partner.crm.user","base.model_res_partner","base.group_sale_salesman",1,1,1,0 "access_res_partner_address","res.partner.address.crm.user","base.model_res_partner_address","base.group_sale_salesman",1,1,1,0 "access_res_partner_category","res.partner.category.crm.user","base.model_res_partner_category","base.group_sale_salesman",1,1,1,0 -"mail_gateway_mailgate_thread","mail_gateway.mailgate.thread","mail_gateway.model_mailgate_thread","base.group_sale_salesman",1,1,1,1 -"mail_gateway_mailgate_message_user","mail_gateway.mailgate.message.user","mail_gateway.model_mailgate_message","base.group_sale_salesman",1,1,1,1 +"mail_mailgate_thread","mail.thread","mail.model_mail_thread","base.group_sale_salesman",1,1,1,1 +"mail_gateway_mail_message_user","mail.message.user","mail.model_mail_message","base.group_sale_salesman",1,1,1,1 "access_crm_case_categ_manager","crm.case.categ manager","model_crm_case_categ","base.group_sale_manager",1,1,1,1 "access_base_action_rule_manager","base.action.rule manager","model_base_action_rule","base.group_sale_manager",1,1,1,1 "access_crm_lead_report_user","crm.lead.report user","model_crm_lead_report","base.group_sale_salesman",1,1,1,1 diff --git a/addons/crm/wizard/__init__.py b/addons/crm/wizard/__init__.py index 9730b8d1d551112ec4797bfa0430255694c84f01..3ebdceda4379504e4cc39d2abab5c1da03a837b3 100644 --- a/addons/crm/wizard/__init__.py +++ b/addons/crm/wizard/__init__.py @@ -19,7 +19,7 @@ # ############################################################################## -import crm_send_email +import mail_compose_message import crm_add_note import crm_lead_to_partner diff --git a/addons/crm/wizard/crm_add_note.py b/addons/crm/wizard/crm_add_note.py index 3419c89d33ad2488fca1b80bc0e0cb448246f6c3..478f051fe5816ff3ba5100571d1930007c4551f7 100644 --- a/addons/crm/wizard/crm_add_note.py +++ b/addons/crm/wizard/crm_add_note.py @@ -1,11 +1,10 @@ from crm import crm from osv import fields, osv from tools.translate import _ -import base64 +from mail.mail_message import truncate_text AVAILABLE_STATES = crm.AVAILABLE_STATES + [('unchanged', 'Unchanged')] - class crm_add_note(osv.osv_memory): """Adds a new note to the case.""" _name = 'crm.add.note' @@ -14,8 +13,11 @@ class crm_add_note(osv.osv_memory): _columns = { 'body': fields.text('Note Body', required=True), 'state': fields.selection(AVAILABLE_STATES, string='Set New State To', - required=True), - 'attachment_ids' : fields.one2many('crm.send.mail.attachment', 'wizard_id'), + required=True), + } + + _defaults = { + 'state': 'unchanged' } def action_add(self, cr, uid, ids, context=None): @@ -32,14 +34,8 @@ class crm_add_note(osv.osv_memory): case_list = case_pool.browse(cr, uid, context['active_ids'], context=context) case = case_list[0] - user_obj = self.pool.get('res.users') - user_name = user_obj.browse(cr, uid, [uid], context=context)[0].name - attach = [ - (x.name, base64.decodestring(x.binary)) for x in obj.attachment_ids - ] - case_pool.history(cr, uid, [case], self.pool.get('mailgate.message').truncate_data(cr, uid, obj.body, context=context), history=False, - details=obj.body, email_from=user_name, attach=attach) - + case_pool.message_append(cr, uid, [case], truncate_text(obj.body), + body_text=obj.body) if obj.state == 'unchanged': pass elif obj.state == 'done': @@ -52,11 +48,4 @@ class crm_add_note(osv.osv_memory): return {'type': 'ir.actions.act_window_close'} - def default_get(self, cr, uid, fields, context=None): - """ - This function gets default values - """ - return {'state': u'unchanged'} - - crm_add_note() diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 16223f8c28fb9587198534ba238028657378646e..8200dbd167526791c6c9b19f5632fc7f65f333eb 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -132,26 +132,25 @@ class crm_lead2opportunity_partner(osv.osv_memory): vals['partner_address_id'] = False lead.write(vals, context=context) - leads.history(cr, uid, [lead], _('Converted to opportunity'), details='Converted to Opportunity', context=context) + text = _('Converted to opportunity') + leads.message_append(cr, uid, [lead], text, body_text=text, context=context) if lead.partner_id: msg_ids = [ x.id for x in lead.message_ids] - self.pool.get('mailgate.message').write(cr, uid, msg_ids, { + self.pool.get('mail.message').write(cr, uid, msg_ids, { 'partner_id': lead.partner_id.id }, context=context) leads.log(cr, uid, lead.id, _("Lead '%s' has been converted to an opportunity.") % lead.name) - def send_mail_to_salesman(self, lead): + def send_mail_to_salesman(self, cr, uid, lead): email_to = lead.user_id and lead.user_id.user_email if not email_to: - return + return False + message_pool = self.pool.get('mail.message') email_from = lead.section_id and lead.section_id.user_id and lead.section_id.user_id.user_email or email_to - partner = lead.partner_id and lead.partner_id.name or lead.partner_name + partner = lead.partner_id and lead.partner_id.name or lead.partner_name subject = "lead %s converted into opportunity" % lead.name - body = "Info \n Id : %s \n Subject: %s \n Partner: %s \n Description : %s " % (lead.id, lead.name, lead.partner_id.name, lead.description) - try : - tools.email_send(email_from, [email_to], subject, body) - except: - pass + body = "Info \n Id : %s \n Subject: %s \n Partner: %s \n Description : %s " % (lead.id, lead.name, lead.partner_id.name, lead.description) + return message_pool.schedule_with_attach(cr, uid, email_from, [email_to], subject, body) def action_apply(self, cr, uid, ids, context=None): """ @@ -205,7 +204,7 @@ class crm_lead2opportunity_partner(osv.osv_memory): partner_id = False self._convert(cr, uid, ids, lead, partner_id, stage_ids, context=context) - self.send_mail_to_salesman(lead) + self.send_mail_to_salesman(cr, uid, lead) #If we convert in mass, don't merge if there is no other opportunity but no warning if data.name == 'merge' and (len(data.opportunity_ids) > 1 or not context.get('mass_convert') ): merge_obj = self.pool.get('crm.merge.opportunity') diff --git a/addons/crm/wizard/crm_merge_opportunities.py b/addons/crm/wizard/crm_merge_opportunities.py index 57620f8c016f8c52a96d03f64b1344b156332ecd..d2d4674974693618a621b61c5bee4049733700e9 100644 --- a/addons/crm/wizard/crm_merge_opportunities.py +++ b/addons/crm/wizard/crm_merge_opportunities.py @@ -49,14 +49,9 @@ class crm_merge_opportunity(osv.osv_memory): def get_attachments(self, cr, uid, id, context=None): - attach_obj = self.pool.get('ir.attachment') - attach_ids = attach_obj.search(cr, uid, [('res_model' , '=', 'crm.lead'), ('res_id', '=', id)]) - return attach_ids - - def set_attachements_res_id(self, cr, uid, op_id, attach_ids, context=None): - attach_obj = self.pool.get('ir.attachment') - attach_obj.write(cr, uid, attach_ids, {'res_id' : op_id}) - + proxy = self.pool.get('ir.attachment') + ids = proxy.search(cr, uid, [('res_model', '=', 'crm.lead'), ('res_id', '=', id)], context=context) + return proxy.browse(cr, uid, ids, context=context) def find_oldest(self, cr, uid, op_ids, context=None): if not context: @@ -70,9 +65,9 @@ class crm_merge_opportunity(osv.osv_memory): return False opps = lead_obj.browse(cr, uid, [op_id[0]], context=context) return opps[0] - + def _update_data(self, op_ids, oldest_opp): - data = { + data = { 'partner_id': self._get_first_not_null_id('partner_id', op_ids, oldest_opp), # !! 'title': self._get_first_not_null_id('title', op_ids, oldest_opp), 'name' : self._get_first_not_null('name', op_ids, oldest_opp), #not lost @@ -105,16 +100,15 @@ class crm_merge_opportunity(osv.osv_memory): 'email_from' : self._get_first_not_null('email_from', op_ids, oldest_opp), 'email_cc' : self._get_first_not_null('email_cc', op_ids, oldest_opp), 'partner_name' : self._get_first_not_null('partner_name', op_ids, oldest_opp), - - } - return data + } + return data def merge(self, cr, uid, op_ids, context=None): """ - @param opp_ids : list of opportunities ids to merge + :param opp_ids: list of opportunities ids to merge """ opp_obj = self.pool.get('crm.lead') - message_obj = self.pool.get('mailgate.message') + message_obj = self.pool.get('mail.message') lead_ids = context and context.get('lead_ids', []) or [] @@ -130,15 +124,23 @@ class crm_merge_opportunity(osv.osv_memory): else: first_opportunity = opportunities_list[0] tail_opportunities = opportunities_list[1:] - - - data = self._update_data(op_ids, oldest_opp) + #copy message into the first opportunity + merge attachement - - for opp in tail_opportunities + [first_opportunity]: - attach_ids = self.get_attachments(cr, uid, opp, context=context) - self.set_attachements_res_id(cr, uid, first_opportunity.id, attach_ids) + count = 1 + first_attachments = self.get_attachments(cr, uid, first_opportunity, context=context) + for opp in tail_opportunities: + attachments = self.get_attachments(cr, uid, opp, context=context) + for first in first_attachments: + for attachment in attachments: + if attachment.name == first.name: + values = dict( + name = "%s (%s)" % (attachment.name, count,), + res_id = first_opportunity.id, + ) + attachment.write(values) + count+=1 + for history in opp.message_ids: message_obj.write(cr, uid, history.id, {'res_id': first_opportunity.id, 'name' : _("From %s : %s") % (opp.name, history.name) }, context=context) @@ -172,16 +174,14 @@ class crm_merge_opportunity(osv.osv_memory): subject = subject[0] + ", ".join(subject[1:]) details = "\n\n".join(details) - opp_obj._history(cr, uid, [first_opportunity], subject, details=details) + opp_obj.message_append(cr, uid, [first_opportunity], subject, body_text=details) #data.update({'message_ids' : [(6, 0 ,self._concat_o2m('message_ids', op_ids))]}) - opp_obj.write(cr, uid, [first_opportunity.id], data) + opp_obj.write(cr, uid, [first_opportunity.id], data, context=context) unlink_ids = map(lambda x: x.id, tail_opportunities) opp_obj.unlink(cr, uid, unlink_ids, context=context) models_data = self.pool.get('ir.model.data') - - # Get Opportunity views opportunity_view_form = models_data._get_id( cr, uid, 'crm', 'crm_case_form_view_oppor') @@ -216,7 +216,6 @@ class crm_merge_opportunity(osv.osv_memory): context['lead_ids'] = [op_ids[0].id] return self.merge(cr, uid, op_ids, context) - _columns = { 'opportunity_ids' : fields.many2many('crm.lead', 'merge_opportunity_rel', 'merge_id', 'opportunity_id', 'Opportunities', domain=[('type', '=', 'opportunity')]), } diff --git a/addons/crm/wizard/crm_send_email.py b/addons/crm/wizard/crm_send_email.py deleted file mode 100644 index d4ba8b476a132aa60c7203d461d3ae1a479d6672..0000000000000000000000000000000000000000 --- a/addons/crm/wizard/crm_send_email.py +++ /dev/null @@ -1,303 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). All Rights Reserved -# $Id$ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -############################################################################## - -from crm import crm -from osv import osv, fields -from tools.translate import _ -import base64 -import itertools -import tools -import re - - -AVAILABLE_STATES = crm.AVAILABLE_STATES + [('unchanged', 'Unchanged')] - - -class crm_send_new_email_attachment(osv.osv_memory): - _name = 'crm.send.mail.attachment' - - _columns = { - 'binary' : fields.binary('Attachment', required=True), - 'name' : fields.char('Name', size=128, required=True), - 'wizard_id' : fields.many2one('crm.send.mail', 'Wizard', required=True), - } - -crm_send_new_email_attachment() - -class crm_send_new_email(osv.osv_memory): - """ Sends new email for the case""" - _name = "crm.send.mail" - _description = "Send new email" - - _columns = { - 'email_to' : fields.char('To', size=512, required=True), - 'email_from' : fields.char('From', size=128, required=True), - 'reply_to' : fields.char('Reply To', size=128, required=True, help="Reply-to of the Sales team defined on this case"), - 'email_cc' : fields.char('CC', size=512, help="These addresses will receive a copy of this email. To modify the permanent CC list, edit the global CC field of this case"), - 'subject': fields.char('Subject', size=512, required=True), - 'body': fields.text('Message Body', required=True), - 'state': fields.selection(AVAILABLE_STATES, string='Set New State To', required=True), - 'attachment_ids' : fields.one2many('crm.send.mail.attachment', 'wizard_id', 'Attachment'), - 'html': fields.boolean('HTML formatting?', help="Select this if you want to send email with HTML formatting."), - } - - def action_mass_send(self, cr, uid, ids, context=None): - - if not context: - context = {} - - context.update({'mail' : 'new'}) - actives_ids = context.get('active_ids') - model = context.get('active_model') - case_pool = self.pool.get(model) - for id in actives_ids: - context.update({'active_id' : id}) - self.action_send(cr, uid, ids, context=context) - - return {'type': 'ir.actions.act_window_close'} - - def action_send(self, cr, uid, ids, context=None): - """ This sends an email to ALL the addresses of the selected partners. - """ - hist_obj = self.pool.get('mailgate.message') - - if context is None: - context = {} - - if not context.get('active_model'): - raise osv.except_osv(_('Error'), _('Can not send mail!')) - - model = context.get('active_model') - case_pool = self.pool.get(model) - res_id = context and context.get('active_id', False) or False - - - - for obj in self.browse(cr, uid, ids, context=context): - attach = [ - (x.name, base64.decodestring(x.binary)) for x in obj.attachment_ids - ] - - subtype = 'plain' - message_id = None - ref_id = None - - case = case_pool.browse(cr, uid, res_id, context=context) - if context.get('mail', 'new') == 'new': - if case.message_ids: - message_id = case.message_ids[0].message_id - elif context.get('mail') == 'forward': - # extract attachements from case and emails according to mode - attachments = [] - attach_pool = self.pool.get('ir.attachment') - direct_attachments = attach_pool.search(cr, uid, [('res_model', '=', 'crm.lead'), ('res_id', '=', res_id)], context=context) - attachments += attach_pool.browse(cr, uid, direct_attachments, context=context) - if obj.history in ['latest', 'whole'] and case.message_ids: - msgs = case.message_ids - if obj.history == 'latest': - msgs = msgs[:1] - attachments.extend(itertools.chain(*[m.attachment_ids for m in msgs])) - attach_all = [(a.datas_fname or a.name, base64.decodestring(a.datas)) for a in attachments if a.datas] - attach += attach_all - - else: - hist = hist_obj.browse(cr, uid, res_id, context=context) - message_id = hist.message_id - model = hist.model - case_pool = self.pool.get(model) - res_id = hist.res_id - ref_id = hist.ref_id - case = case_pool.browse(cr, uid, res_id, context=context) - - if context.get('mass_mail'): - email_temp = case.email_from and tools.ustr(case.email_from) or '' - emails = re.findall(r'([^ ,<@]+@[^> ,]+)', email_temp) - else: - emails = re.findall(r'([^ ,<@]+@[^> ,]+)', obj.email_to or '') - - email_cc = re.findall(r'([^ ,<@]+@[^> ,]+)', obj.email_cc or '') - - emails = filter(None, emails) - body = obj.body - - body = body and tools.ustr(body) or '' - email_from = getattr(obj, 'email_from', False) - x_headers = {} - if message_id: - x_headers['References'] = "%s" % (message_id) - - if obj.html: - subtype = 'html' - - flag = tools.email_send( - email_from, - emails, - obj.subject, - body, - email_cc=email_cc, - attach=attach, - subtype=subtype, - reply_to=obj.reply_to, - openobject_id=str(case.id), - x_headers=x_headers - ) - - if not flag: - raise osv.except_osv(_('Error!'), _('Unable to send mail. Please check SMTP is configured properly.')) - - msg_dict = {'new': 'Send', 'reply': 'Reply', 'forward': 'Forward'} - case_pool.history(cr, uid, [case], _(msg_dict[context.get('mail', 'new')]), history=True, \ - email=obj.email_to, details=body, \ - subject=obj.subject, email_from=email_from, \ - email_cc=', '.join(email_cc), message_id=message_id, \ - references=ref_id or message_id, attach=attach) - if obj.state == 'unchanged': - pass - elif obj.state == 'done': - case_pool.case_close(cr, uid, [case.id]) - elif obj.state == 'draft': - case_pool.case_reset(cr, uid, [case.id]) - elif obj.state in ['cancel', 'open', 'pending']: - act = 'case_' + obj.state - getattr(case_pool, act)(cr, uid, [case.id]) - - return {'type': 'ir.actions.act_window_close'} - - def default_get(self, cr, uid, fields, context=None): - """ - This function gets default values - """ - if context is None: - context = {} - - if not context.get('active_model'): - raise osv.except_osv(_('Error'), _('Can not send mail!')) - - res = super(crm_send_new_email, self).default_get(cr, uid, fields, context=context) - - if context.get('mail') == 'reply': - res.update(self.get_reply_defaults(cr, uid, fields, context=context)) - return res - - model = context.get('active_model') - mod_obj = self.pool.get(model) - res_id = context and context.get('active_ids', []) or [] - - user_obj = self.pool.get('res.users') - user_mail_from = user_obj._get_email_from(cr, uid, [uid], context=context)[uid] - for case in mod_obj.browse(cr, uid, res_id, context=context): - if 'email_to' in fields: - res.update({'email_to': case.email_from and tools.ustr(case.email_from) or ''}) - if context.get('mass_mail'): - res.update({'email_to': ''}) - if 'email_from' in fields: - res.update({'email_from': user_mail_from and tools.ustr(user_mail_from) or ''}) - if 'reply_to' in fields: - if hasattr(case, 'section_id'): - res.update({'reply_to': case.section_id and case.section_id.reply_to or False}) - if 'subject' in fields: - res.update({'subject': tools.ustr(context.get('subject', case.name) or '')}) - if context.get('mass_mail'): - res.update({'subject': ''}) - if 'email_cc' in fields: - res.update({'email_cc': tools.ustr(case.email_cc or '')}) - if context.get('mass_mail'): - res.update({'email_cc': ''}) - if 'body' in fields: - res.update({'body': u'\n'+(tools.ustr(case.user_id.signature or ''))}) - if 'state' in fields: - res.update({'state': u'pending'}) - - return res - - def get_reply_defaults(self, cr, uid, fields, context=None): - """ - This function gets default values for reply mail - """ - hist_obj = self.pool.get('mailgate.message') - res_ids = context and context.get('active_ids', []) or [] - - user_obj = self.pool.get('res.users') - user_mail_from = user_obj._get_email_from(cr, uid, [uid], context=context)[uid] - - include_original = context and context.get('include_original', False) or False - res = {} - for hist in hist_obj.browse(cr, uid, res_ids, context=context): - model = hist.model - - # In the case where the crm.case does not exist in the database - if not model: - return {'type': 'ir.actions.act_window_close'} - - model_pool = self.pool.get(model) - res_id = hist.res_id - case = model_pool.browse(cr, uid, res_id) - if 'email_to' in fields: - res.update({'email_to': case.email_from and tools.ustr(case.email_from) or False}) - if 'email_from' in fields: - res.update({'email_from': user_mail_from and tools.ustr(user_mail_from) or False}) - - signature = u'\n' + (tools.ustr(case.user_id.signature or '')) + u'\n' - original = [signature] - - if include_original == True and 'body' in fields: - header = u'-------- Original Message --------' - sender = u'From: %s' %(tools.ustr(hist.email_from or '')) - to = u'To: %s' % (tools.ustr(hist.email_to or '')) - sentdate = u'Date: %s' % (tools.ustr(hist.date)) - desc = u'\n%s'%(tools.ustr(hist.description)) - - original = [signature, header, sender, to, sentdate, desc] - - res['body']= u'\n' + u'\n'.join(original) - - if 'subject' in fields: - res.update({u'subject': u'Re: %s' %(tools.ustr(hist.name or ''))}) - if 'email_cc' in fields: - email_cc = (case.email_cc and tools.ustr(case.email_cc) + ', ' or '') + (hist.email_cc or '') - res.update({'email_cc': email_cc}) - if 'reply_to' in fields: - if hasattr(case, 'section_id'): - res.update({'reply_to': case.section_id.reply_to or ''}) - if 'state' in fields: - res['state'] = u'pending' - return res - - def view_init(self, cr, uid, fields_list, context=None): - """ - This function checks for precondition before wizard executes - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param fields: List of fields for default value - @param context: A standard dictionary for contextual values - - """ - if context is None: - context = {} - - if not context.get('active_model'): - raise osv.except_osv(_('Error'), _('Can not send mail!')) - return True - -crm_send_new_email() - diff --git a/addons/crm/wizard/crm_send_email_view.xml b/addons/crm/wizard/crm_send_email_view.xml deleted file mode 100644 index be1644b6bbba4945b01d4b28093da6ff382bd810..0000000000000000000000000000000000000000 --- a/addons/crm/wizard/crm_send_email_view.xml +++ /dev/null @@ -1,154 +0,0 @@ -<?xml version="1.0"?> -<openerp> - <data> - -<!-- Send New Mail view --> - - <record model="ir.ui.view" id="crm_send_new_mail_view"> - <field name="name">crm.new.send.mail.form</field> - <field name="model">crm.send.mail</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Send Mail" col="4"> - <group colspan="4" col="2"> - <field name="email_from"/> - <field name="reply_to"/> - <field name="email_to" /> - <field name="email_cc"/> - <field name="subject"/> - <field name="html"/> - </group> - <notebook colspan="6"> - <page string="Message"> - <field name="body" nolabel="1" colspan="4" default_focus="1"/> - </page> - <page string="Attachments"> - <field name="attachment_ids" colspan="4" nolabel="1"> - <form string="Attachment"> - <field name="binary" filename="name" /> - <field name="name" /> - </form> - <tree string="Attachments"> - <field name="name" /> - </tree> - </field> - </page> - </notebook> - <separator string="" colspan="6"/> - <group colspan="6" col="4" > - <field name="state" /> - <button string="_Cancel" icon="gtk-close" special="cancel" /> - <button name="action_send" type="object" string="_Send" icon="gtk-go-forward" /> - </group> - </form> - </field> - </record> - - <record model="ir.ui.view" id="crm_send_new_mass_mail_view"> - <field name="name">crm.new.send.mass.mail.form</field> - <field name="model">crm.send.mail</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Send Mail" col="4"> - <group colspan="4" col="2"> - <field name="email_from"/> - <field name="reply_to"/> - <field name="email_cc"/> - <field name="subject" /> - <field name="html"/> - </group> - <notebook colspan="6"> - <page string="Message"> - <field name="body" nolabel="1" colspan="4" default_focus="1"/> - </page> - <page string="Attachments"> - <field name="attachment_ids" colspan="4" nolabel="1"> - <form string="Attachment"> - <field name="binary" filename="name" /> - <field name="name" /> - </form> - <tree string="Attachments"> - <field name="name" /> - </tree> - </field> - </page> - </notebook> - <separator string="" colspan="6"/> - <group colspan="6" col="4" > - <field name="state" /> - <button string="_Cancel" icon="gtk-cancel" special="cancel" /> - <button name="action_mass_send" type="object" string="_Send to All" icon="gtk-go-forward" /> - </group> - </form> - </field> - </record> - -<!-- Send New Mail action --> - - <record model="ir.actions.act_window" id="action_crm_send_mail"> - <field name="name">Send Mail</field> - <field name="res_model">crm.send.mail</field> - <field name="view_type">form</field> - <field name="view_mode">form</field> - <field name="view_id" ref="crm_send_new_mail_view"/> - <field name="target">new</field> - </record> - - - <act_window id="action_crm_send_mass_mail" - multi="True" - key2="client_action_multi" name="Send emails" - res_model="crm.send.mail" src_model="crm.lead" - view_mode="form" target="new" view_type="form" - context="{'mass_mail' : True}" - view_id="crm_send_new_mass_mail_view"/> - - <!-- Reply to Mail view --> - - <record model="ir.ui.view" id="crm_reply_mail_view"> - <field name="name">crm.mail.reply.form</field> - <field name="model">crm.send.mail</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Reply to last Mail" col="2"> - <field name="email_from" /> - <field name="email_to" /> - <field name="email_cc" /> - <field name="subject" /> - <field name="attachment_ids" colspan="4" nolabel="1"> - <form string="Attachment"> - <field name="binary" filename="name" /> - <field name="name" /> - </form> - <tree string="Attachments"> - <field name="name" /> - </tree> - </field> - <separator string="" colspan="4"/> - <field name="body" nolabel="1" colspan="4"/> - <separator string=" " colspan="4"/> - <group colspan="4" col="3" > - <label string=" " /> - <button string="_Cancel" icon="gtk-close" special="cancel" /> - <button name="action_send" type="object" string="_Send Reply" icon="gtk-go-forward" /> - </group> - </form> - </field> - </record> - - - - -<!-- Reply to Mail action --> - - <record model="ir.actions.act_window" id="action_crm_reply_mail"> - <field name="name">Reply to last Mail</field> - <field name="res_model">crm.send.mail</field> - <field name="view_type">form</field> - <field name="view_mode">form</field> - <field name="view_id" ref="crm_reply_mail_view"/> - <field name="target">new</field> - </record> - - </data> -</openerp> diff --git a/addons/crm/wizard/mail_compose_message.py b/addons/crm/wizard/mail_compose_message.py new file mode 100644 index 0000000000000000000000000000000000000000..512d9ece77a2ff223220c67bc24ed330d44527b5 --- /dev/null +++ b/addons/crm/wizard/mail_compose_message.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +from osv import osv +from osv import fields +import tools + +SUPPORTED_MODELS = ['crm.lead',] + +class mail_compose_message(osv.osv_memory): + _inherit = 'mail.compose.message' + + def get_value(self, cr, uid, model, res_id, context=None): + """Returns a defaults-like dict with initial values for the composition + wizard when sending an email related to the document record identified + by ``model`` and ``res_id``. + + Overrides the default implementation to provide more default field values + related to the corresponding CRM case. + + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + """ + result = super(mail_compose_message, self).get_value(cr, uid, model, res_id, context=context) + if model in SUPPORTED_MODELS and res_id: + model_obj = self.pool.get(model) + data = model_obj.browse(cr, uid , res_id, context) + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + result.update({ + 'subject' : data.name or False, + 'email_to' : data.email_from or False, + 'email_from' : user.user_email or tools.config.get('email_from', False), + 'body_text' : '\n' + tools.ustr(user.signature), + 'email_cc' : tools.ustr(data.email_cc or ''), + 'model': model, + 'res_id': res_id, + 'subtype': 'plain', + }) + if hasattr(data, 'section_id'): + result.update({'reply_to' : data.section_id and data.section_id.reply_to or False}) + return result + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/wizard/wizard_history_event.py b/addons/crm/wizard/wizard_history_event.py index caaf37a88d857976284a658c889666218259fd76..bb501e0b750de08e613c7c7dfb7c4cc4241990d5 100644 --- a/addons/crm/wizard/wizard_history_event.py +++ b/addons/crm/wizard/wizard_history_event.py @@ -32,7 +32,7 @@ def _open_history_event(self, cr, uid, data, context=None): if id2: id2 = data_obj.browse(cr, uid, id2, context=context).res_id res = '' - if data.get('model',False) and data.get('ids',False): + if data.get('model') and data.get('ids'): model_obj = pooler.get_pool(cr.dbname).get(data['model']) res = model_obj.browse(cr, uid, data['ids'], context=context) if len(res): diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py index 401c43b0455a32459587849806ba8b650bd52433..9700a5a24b28d2d0ac0118a676f4175560056df8 100644 --- a/addons/crm_claim/crm_claim.py +++ b/addons/crm_claim/crm_claim.py @@ -22,10 +22,12 @@ from osv import fields, osv from crm import crm import time +from crm import wizard import binascii import tools from tools.translate import _ +wizard.mail_compose_message.SUPPORTED_MODELS.append('crm.claim') CRM_CLAIM_PENDING_STATES = ( crm.AVAILABLE_STATES[2][0], # Cancelled crm.AVAILABLE_STATES[3][0], # Done @@ -40,69 +42,69 @@ class crm_claim(crm.crm_case, osv.osv): _name = "crm.claim" _description = "Claim" _order = "priority,date desc" - _inherit = ['mailgate.thread'] + _inherit = ['mail.thread'] _columns = { - 'id': fields.integer('ID', readonly=True), - 'name': fields.char('Claim Subject', size=128, required=True), + 'id': fields.integer('ID', readonly=True), + 'name': fields.char('Claim Subject', size=128, required=True), 'action_next': fields.char('Next Action', size=200), 'date_action_next': fields.datetime('Next Action Date'), - 'description': fields.text('Description'), - 'resolution': fields.text('Resolution'), - 'create_date': fields.datetime('Creation Date' , readonly=True), - 'write_date': fields.datetime('Update Date' , readonly=True), - 'date_deadline': fields.date('Deadline'), - 'date_closed': fields.datetime('Closed', readonly=True), - 'date': fields.datetime('Claim Date'), - 'ref' : fields.reference('Reference', selection=crm._links_get, size=128), + 'description': fields.text('Description'), + 'resolution': fields.text('Resolution'), + 'create_date': fields.datetime('Creation Date' , readonly=True), + 'write_date': fields.datetime('Update Date' , readonly=True), + 'date_deadline': fields.date('Deadline'), + 'date_closed': fields.datetime('Closed', readonly=True), + 'date': fields.datetime('Claim Date'), + 'ref' : fields.reference('Reference', selection=crm._links_get, size=128), 'categ_id': fields.many2one('crm.case.categ', 'Category', \ domain="[('section_id','=',section_id),\ - ('object_id.model', '=', 'crm.claim')]"), - 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), + ('object_id.model', '=', 'crm.claim')]"), + 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), 'type_action': fields.selection([('correction','Corrective Action'),('prevention','Preventive Action')], 'Action Type'), - 'user_id': fields.many2one('res.users', 'Responsible'), - 'user_fault': fields.char('Trouble Responsible', size=64), + 'user_id': fields.many2one('res.users', 'Responsible'), + 'user_fault': fields.char('Trouble Responsible', size=64), 'section_id': fields.many2one('crm.case.section', 'Sales Team', \ select=True, help="Sales team to which Case belongs to."\ "Define Responsible user and Email account for"\ - " mail gateway."), - 'company_id': fields.many2one('res.company', 'Company'), - 'partner_id': fields.many2one('res.partner', 'Partner'), + " mail gateway."), + 'company_id': fields.many2one('res.company', 'Company'), + 'partner_id': fields.many2one('res.partner', 'Partner'), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \ # domain="[('partner_id','=',partner_id)]" - ), - 'email_cc': fields.text('Watchers Emails', size=252, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), - 'email_from': fields.char('Email', size=128, help="These people will receive email."), - 'partner_phone': fields.char('Phone', size=32), + ), + 'email_cc': fields.text('Watchers Emails', size=252, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), + 'email_from': fields.char('Email', size=128, help="These people will receive email."), + 'partner_phone': fields.char('Phone', size=32), 'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('section_ids','=',section_id)]"), - 'cause': fields.text('Root Cause'), - 'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True, + 'cause': fields.text('Root Cause'), + 'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True, help='The state is set to \'Draft\', when a case is created.\ \nIf the case is in progress the state is set to \'Open\'.\ \nWhen the case is over, the state is set to \'Done\'.\ - \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), } _defaults = { - 'user_id': crm.crm_case._get_default_user, - 'partner_id': crm.crm_case._get_default_partner, - 'partner_address_id': crm.crm_case._get_default_partner_address, - 'email_from':crm.crm_case. _get_default_email, - 'state': lambda *a: 'draft', - 'section_id': crm.crm_case._get_section, + 'user_id': crm.crm_case._get_default_user, + 'partner_id': crm.crm_case._get_default_partner, + 'partner_address_id': crm.crm_case._get_default_partner_address, + 'email_from':crm.crm_case. _get_default_email, + 'state': lambda *a: 'draft', + 'section_id':crm.crm_case. _get_section, 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c), + 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c), 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], } - + def onchange_partner_id(self, cr, uid, ids, part, email=False): """This function returns value of partner address based on partner - @param part: Partner's id - @email: Partner's email ID + :param part: Partner's id + :param email: ignored """ if not part: return {'value': {'partner_address_id': False, - 'email_from': False, + 'email_from': False, 'partner_phone': False, 'partner_mobile': False }} @@ -113,8 +115,8 @@ class crm_claim(crm.crm_case, osv.osv): def onchange_partner_address_id(self, cr, uid, ids, add, email=False): """This function returns value of partner email based on Partner Address - @param add: Id of Partner's address - @email: Partner's email ID + :param part: Partner's id + :param email: ignored """ if not add: return {'value': {'email_from': False}} @@ -122,6 +124,7 @@ class crm_claim(crm.crm_case, osv.osv): return {'value': {'email_from': address.email, 'partner_phone': address.phone, 'partner_mobile': address.mobile}} def case_open(self, cr, uid, ids, *args): + """Opens Claim""" for l in self.browse(cr, uid, ids): # When coming from draft override date and stage otherwise just set state if l.state == 'draft': @@ -135,16 +138,13 @@ class crm_claim(crm.crm_case, osv.osv): res = super(crm_claim, self).case_open(cr, uid, ids, *args) return res - def message_new(self, cr, uid, msg, context=None): - """ Automatically calls when new email message arrives - """ - mailgate_pool = self.pool.get('email.server.tools') - - subject = msg.get('subject') or _("No Subject") - body = msg.get('body') + def message_new(self, cr, uid, msg, custom_values=None, context=None): + """Automatically called when new email message arrives""" + res_id = super(crm_claim,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) + subject = msg.get('subject') + body = msg.get('body_text') msg_from = msg.get('from') priority = msg.get('priority') - vals = { 'name': subject, 'email_from': msg_from, @@ -152,35 +152,18 @@ class crm_claim(crm.crm_case, osv.osv): 'description': body, 'user_id': False, } - if msg.get('priority', False): + if priority: vals['priority'] = priority + vals.update(self.message_partner_by_email(cr, uid, msg.get('from', False))) + self.write(cr, uid, [res_id], vals, context=context) + return res_id - res = mailgate_pool.get_partner(cr, uid, msg.get('from') or msg.get_unixfrom()) - if res: - vals.update(res) - - res = self.create(cr, uid, vals, context) - attachents = msg.get('attachments', []) - for attactment in attachents or []: - data_attach = { - 'name': attactment, - 'datas':binascii.b2a_base64(str(attachents.get(attactment))), - 'datas_fname': attactment, - 'description': 'Mail attachment', - 'res_model': self._name, - 'res_id': res, - } - self.pool.get('ir.attachment').create(cr, uid, data_attach) - - return res - - def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context=None): - """ - @param ids: List of update mail’s IDs - """ + def message_update(self, cr, uid, ids, msg, vals={}, default_act='pending', context=None): if isinstance(ids, (str, int, long)): ids = [ids] + res_id = super(crm_claim,self).message_update(cr, uid, ids, msg, context=context) + if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): vals['priority'] = msg.get('priority') @@ -190,7 +173,7 @@ class crm_claim(crm.crm_case, osv.osv): 'probability':'probability' } vls = {} - for line in msg['body'].split('\n'): + for line in msg['body_text'].split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower()): @@ -208,19 +191,10 @@ class crm_claim(crm.crm_case, osv.osv): res = self.write(cr, uid, [case.id], values, context=context) return res - def msg_send(self, cr, uid, id, *args, **argv): - """ Send The Message - @param ids: List of email’s IDs - """ - return True - -crm_claim() - class res_partner(osv.osv): _inherit = 'res.partner' _columns = { 'claims_ids': fields.one2many('crm.claim', 'partner_id', 'Claims'), } -res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm_claim/crm_claim_view.xml b/addons/crm_claim/crm_claim_view.xml index 72dcabf7c9a2f45ae4660aec7ed0cac333b03f89..21ec4cf028e6bfa9ebc27e8f703bf63ed9e988f0 100644 --- a/addons/crm_claim/crm_claim_view.xml +++ b/addons/crm_claim/crm_claim_view.xml @@ -70,15 +70,10 @@ <group> <field name="name" /> <field name="date"/> - - </group> - <group colspan="4" col="6"> - <field name="user_id"/> <field name="section_id" widget="selection" /> - <group colspan="2" col="4"> <field name="stage_id" domain="[('section_ids','=',section_id)]"/> <button name="stage_previous" string="" type="object" icon="gtk-go-back" /> @@ -159,12 +154,12 @@ <field name="message_ids" colspan="4" nolabel="1" mode="tree,form" readonly="1"> <tree string="History"> <field name="display_text" string="History Information"/> - <field name="history" invisible="1"/> - <button - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.claim', 'include_original' : True}" - icon="terp-mail-replied" type="action" attrs="{'invisible': [('history', '!=', True)]}" /> + <field name="email_from" invisible="1"/> + <button + string="Reply" attrs="{'invisible': [('email_from', '=', False)]}" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply'}" + icon="terp-mail-replied" type="action" /> </tree> <form string="Communication & History"> <group col="4" colspan="4"> @@ -172,20 +167,18 @@ <field name="date"/> <field name="email_to" widget="char" size="512"/> <field name="email_cc" widget="char" size="512"/> - <field name="name" colspan="4" widget="char" size="512"/> - <field name="history" invisible="1"/> + <field name="subject" colspan="4" widget="char" size="512"/> </group> <notebook colspan="4"> <page string="Details"> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <field name="description" colspan="4" nolabel="1" height="250"/> - <button colspan="4" - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.claim', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1" height="250"/> + <button colspan="4" string="Reply" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action"/> </group> - <group attrs="{'invisible': [('history', '=', True)]}"> + <group attrs="{'invisible': [('email_from', '!=', False)]}"> <field name="display_text" colspan="4" nolabel="1" height="250"/> </group> </page> @@ -199,10 +192,9 @@ name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action" /> - <button string="Send New Email" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'crm.claim'}" - icon="terp-mail-message-new" type="action" /> + <button string="Send New Email" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action"/> </page> </notebook> </group> diff --git a/addons/crm_claim/report/crm_claim_report.py b/addons/crm_claim/report/crm_claim_report.py index 87eb8a529bca6818cf54bd9b326a28494dc9c9a5..bb9642af1e116abf582079d2db821aee0a096090 100644 --- a/addons/crm_claim/report/crm_claim_report.py +++ b/addons/crm_claim/report/crm_claim_report.py @@ -60,7 +60,7 @@ class crm_claim_report(osv.osv): ('11', 'November'), ('12', 'December')], 'Month', readonly=True), 'company_id': fields.many2one('res.company', 'Company', readonly=True), 'create_date': fields.datetime('Create Date', readonly=True, select=True), - 'day': fields.char('Day', size=128, readonly=True), + 'day': fields.char('Day', size=128, readonly=True), 'delay_close': fields.float('Delay to close', digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"), 'stage_id': fields.many2one ('crm.case.stage', 'Stage', readonly=True,domain="[('section_ids','=',section_id)]"), 'categ_id': fields.many2one('crm.case.categ', 'Category',\ @@ -71,8 +71,8 @@ class crm_claim_report(osv.osv): 'company_id': fields.many2one('res.company', 'Company', readonly=True), 'priority': fields.selection(AVAILABLE_PRIORITIES, 'Priority'), 'type_action': fields.selection([('correction','Corrective Action'),('prevention','Preventive Action')], 'Action Type'), - 'date_closed': fields.date('Close Date', readonly=True, select=True), - 'date_deadline': fields.date('Deadline', readonly=True, select=True), + 'date_closed': fields.date('Close Date', readonly=True, select=True), + 'date_deadline': fields.date('Deadline', readonly=True, select=True), 'delay_expected': fields.float('Overpassed Deadline',digits=(16,2),readonly=True, group_operator="avg"), 'email': fields.integer('# Emails', size=128, readonly=True), 'probability': fields.float('Probability',digits=(16,2),readonly=True, group_operator="avg") @@ -106,7 +106,7 @@ class crm_claim_report(osv.osv): c.type_action as type_action, date_trunc('day',c.create_date) as create_date, avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close, - (SELECT count(id) FROM mailgate_message WHERE model='crm.claim' AND res_id=c.id AND history=True) AS email, + (SELECT count(id) FROM mail_message WHERE model='crm.claim' AND res_id=c.id AND email_from IS NOT NULL) AS email, (SELECT avg(probability) FROM crm_case_stage WHERE id=c.stage_id) AS probability, extract('epoch' from (c.date_deadline - c.date_closed))/(3600*24) as delay_expected from diff --git a/addons/crm_fundraising/crm_fundraising.py b/addons/crm_fundraising/crm_fundraising.py index 01234dbf4364c0ccd25c8aa44eb5dd278a3f8eca..a935162e98eef010ada00d54ca28b3a1271c599b 100644 --- a/addons/crm_fundraising/crm_fundraising.py +++ b/addons/crm_fundraising/crm_fundraising.py @@ -21,6 +21,9 @@ from osv import fields, osv from crm import crm +from crm import wizard + +wizard.mail_compose_message.SUPPORTED_MODELS.append('crm.fundraising') class crm_fundraising(crm.crm_case, osv.osv): """ Fund Raising Cases """ @@ -28,67 +31,84 @@ class crm_fundraising(crm.crm_case, osv.osv): _name = "crm.fundraising" _description = "Fund Raising" _order = "id desc" - _inherit = ['mailgate.thread'] + _inherit = ['mail.thread'] _columns = { - 'id': fields.integer('ID'), + 'id': fields.integer('ID'), 'name': fields.char('Name', size=128, required=True), - 'active': fields.boolean('Active', required=False), + 'active': fields.boolean('Active', required=False), 'date_action_last': fields.datetime('Last Action', readonly=1), - 'date_action_next': fields.datetime('Next Action', readonly=1), - 'description': fields.text('Description'), - 'create_date': fields.datetime('Creation Date' , readonly=True), - 'write_date': fields.datetime('Update Date' , readonly=True), - 'date_deadline': fields.date('Deadline'), - 'user_id': fields.many2one('res.users', 'Responsible'), + 'date_action_next': fields.datetime('Next Action', readonly=1), + 'description': fields.text('Description'), + 'create_date': fields.datetime('Creation Date' , readonly=True), + 'write_date': fields.datetime('Update Date' , readonly=True), + 'date_deadline': fields.date('Deadline'), + 'user_id': fields.many2one('res.users', 'Responsible'), 'section_id': fields.many2one('crm.case.section', 'Sales Team', \ - select=True, help='Sales team to which Case belongs to. Define Responsible user and Email account for mail gateway.'), - 'company_id': fields.many2one('res.company', 'Company'), - 'partner_id': fields.many2one('res.partner', 'Partner'), + select=True, help='Sales team to which Case belongs to. Define Responsible user and Email account for mail gateway.'), + 'company_id': fields.many2one('res.company', 'Company'), + 'partner_id': fields.many2one('res.partner', 'Partner'), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \ - domain="[('partner_id','=',partner_id)]"), - 'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), - 'email_from': fields.char('Email', size=128, help="These people will receive email."), - 'date_closed': fields.datetime('Closed', readonly=True), - 'date': fields.datetime('Date'), - 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), + domain="[('partner_id','=',partner_id)]"), + 'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), + 'email_from': fields.char('Email', size=128, help="These people will receive email."), + 'date_closed': fields.datetime('Closed', readonly=True), + 'date': fields.datetime('Date'), + 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), 'categ_id': fields.many2one('crm.case.categ', 'Category', \ domain="[('section_id','=',section_id),\ - ('object_id.model', '=', 'crm.fundraising')]"), - 'planned_revenue': fields.float('Planned Revenue'), - 'planned_cost': fields.float('Planned Costs'), - 'probability': fields.float('Probability (%)'), - 'partner_name': fields.char("Employee's Name", size=64), - 'partner_name2': fields.char('Employee Email', size=64), - 'partner_phone': fields.char('Phone', size=32), - 'partner_mobile': fields.char('Mobile', size=32), + ('object_id.model', '=', 'crm.fundraising')]"), + 'planned_revenue': fields.float('Planned Revenue'), + 'planned_cost': fields.float('Planned Costs'), + 'probability': fields.float('Probability (%)'), + 'partner_name': fields.char("Employee's Name", size=64), + 'partner_name2': fields.char('Employee Email', size=64), + 'partner_phone': fields.char('Phone', size=32), + 'partner_mobile': fields.char('Mobile', size=32), 'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('section_ids', '=', section_id)]"), 'type_id': fields.many2one('crm.case.resource.type', 'Campaign', \ - domain="[('section_id','=',section_id)]"), - 'duration': fields.float('Duration'), - 'ref': fields.reference('Reference', selection=crm._links_get, size=128), - 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), - 'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True, + domain="[('section_id','=',section_id)]"), + 'duration': fields.float('Duration'), + 'ref': fields.reference('Reference', selection=crm._links_get, size=128), + 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), + 'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True, help='The state is set to \'Draft\', when a case is created.\ \nIf the case is in progress the state is set to \'Open\'.\ \nWhen the case is over, the state is set to \'Done\'.\ - \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), } + + def message_new(self, cr, uid, msg, custom_values=None, context=None): + """Automatically called when new email message arrives""" + res_id = super(crm_fundraising,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) + vals = { + 'name': msg.get('subject'), + 'email_from': msg.get('from'), + 'email_cc': msg.get('cc'), + 'description': msg.get('body_text'), + } + priority = msg.get('priority') + if priority: + vals['priority'] = priority + vals.update(self.message_partner_by_email(cr, uid, msg.get('from'))) + self.write(cr, uid, [res_id], vals, context=context) + return res_id + + _defaults = { - 'active': lambda *a: 1, - 'user_id': crm.crm_case._get_default_user, - 'partner_id': crm.crm_case._get_default_partner, - 'partner_address_id': crm.crm_case._get_default_partner_address, - 'email_from': crm.crm_case. _get_default_email, - 'state': lambda *a: 'draft', - 'section_id': crm.crm_case. _get_section, - 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c), - 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], - 'probability': lambda *a:0.0, - 'planned_cost': lambda *a:0.0, - 'planned_revenue': lambda *a:0.0, - } + 'active': 1, + 'user_id': crm.crm_case._get_default_user, + 'partner_id': crm.crm_case._get_default_partner, + 'partner_address_id': crm.crm_case._get_default_partner_address, + 'email_from': crm.crm_case. _get_default_email, + 'state': 'draft', + 'section_id': crm.crm_case. _get_section, + 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c), + 'priority': crm.AVAILABLE_PRIORITIES[2][0], + 'probability': 0.0, + 'planned_cost': 0.0, + 'planned_revenue': 0.0, + } -crm_fundraising() diff --git a/addons/crm_fundraising/crm_fundraising_view.xml b/addons/crm_fundraising/crm_fundraising_view.xml index c98a06eda4624000cd5f1c579fd4093bd9800949..a527eddd1fd7dc2b8ba67b752179c09425761474 100644 --- a/addons/crm_fundraising/crm_fundraising_view.xml +++ b/addons/crm_fundraising/crm_fundraising_view.xml @@ -136,15 +136,15 @@ <group colspan="4"> <field colspan="4" name="email_cc" string="Global CC" widget="char"/> </group> - <field name="message_ids" colspan="4" nolabel="1" mode="tree,form"> + <field name="message_ids" colspan="4" nolabel="1" mode="tree,form" readonly="1"> <tree string="History"> <field name="display_text" string="History Information"/> - <field name="history" invisible="1"/> - <button - string="Reply" attrs="{'invisible': [('history', '!=', True)]}" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.fundraising', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <field name="email_from" invisible="1"/> + <button + string="Reply" attrs="{'invisible': [('email_from', '=', False)]}" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action" /> </tree> <form string="History"> <group col="4" colspan="4"> @@ -152,20 +152,18 @@ <field name="date"/> <field name="email_to" widget="char" size="512"/> <field name="email_cc" widget="char" size="512"/> - <field name="name" colspan="4" widget="char" size="512"/> - <field name="history" invisible="1"/> + <field name="subject" colspan="4" widget="char" size="512"/> </group> <notebook colspan="4"> <page string="Details"> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <field name="description" colspan="4" nolabel="1" height="250"/> - <button colspan="4" - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.fundraising', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1" height="250"/> + <button colspan="4" string="Reply" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action"/> </group> - <group attrs="{'invisible': [('history', '=', True)]}"> + <group attrs="{'invisible': [('email_from', '!=', False)]}"> <field name="display_text" colspan="4" nolabel="1" height="250"/> </group> </page> @@ -179,10 +177,9 @@ name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action" /> - <button string="Send New Email" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'crm.fundraising'}" - icon="terp-mail-message-new" type="action" /> + <button string="Send New Email" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action"/> </page> <page string="Extra Info" groups="base.group_extended"> <group col="2" colspan="2"> diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py index b27fa04a2af94666a9c5f9f63a333c07ab42c986..48bc2a1bb2e621a910387ae9fedde98d699e3ad2 100644 --- a/addons/crm_helpdesk/crm_helpdesk.py +++ b/addons/crm_helpdesk/crm_helpdesk.py @@ -22,7 +22,7 @@ from crm import crm from osv import fields, osv import time -import binascii +from crm import wizard import tools from tools.translate import _ @@ -32,121 +32,92 @@ CRM_HELPDESK_STATES = ( crm.AVAILABLE_STATES[4][0], # Pending ) +wizard.mail_compose_message.SUPPORTED_MODELS.append('crm.helpdesk') + class crm_helpdesk(crm.crm_case, osv.osv): """ Helpdesk Cases """ _name = "crm.helpdesk" _description = "Helpdesk" _order = "id desc" - _inherit = ['mailgate.thread'] + _inherit = ['mail.thread'] _columns = { - 'id': fields.integer('ID', readonly=True), - 'name': fields.char('Name', size=128, required=True), - 'active': fields.boolean('Active', required=False), - 'date_action_last': fields.datetime('Last Action', readonly=1), - 'date_action_next': fields.datetime('Next Action', readonly=1), - 'description': fields.text('Description'), - 'create_date': fields.datetime('Creation Date' , readonly=True), - 'write_date': fields.datetime('Update Date' , readonly=True), - 'date_deadline': fields.date('Deadline'), - 'user_id': fields.many2one('res.users', 'Responsible'), + 'id': fields.integer('ID', readonly=True), + 'name': fields.char('Name', size=128, required=True), + 'active': fields.boolean('Active', required=False), + 'date_action_last': fields.datetime('Last Action', readonly=1), + 'date_action_next': fields.datetime('Next Action', readonly=1), + 'description': fields.text('Description'), + 'create_date': fields.datetime('Creation Date' , readonly=True), + 'write_date': fields.datetime('Update Date' , readonly=True), + 'date_deadline': fields.date('Deadline'), + 'user_id': fields.many2one('res.users', 'Responsible'), 'section_id': fields.many2one('crm.case.section', 'Sales Team', \ select=True, help='Sales team to which Case belongs to.\ - Define Responsible user and Email account for mail gateway.'), - 'company_id': fields.many2one('res.company', 'Company'), - 'date_closed': fields.datetime('Closed', readonly=True), - 'partner_id': fields.many2one('res.partner', 'Partner'), + Define Responsible user and Email account for mail gateway.'), + 'company_id': fields.many2one('res.company', 'Company'), + 'date_closed': fields.datetime('Closed', readonly=True), + 'partner_id': fields.many2one('res.partner', 'Partner'), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \ - domain="[('partner_id','=',partner_id)]"), - 'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), - 'email_from': fields.char('Email', size=128, help="These people will receive email."), - 'date': fields.datetime('Date'), - 'ref' : fields.reference('Reference', selection=crm._links_get, size=128), - 'ref2' : fields.reference('Reference 2', selection=crm._links_get, size=128), + domain="[('partner_id','=',partner_id)]"), + 'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), + 'email_from': fields.char('Email', size=128, help="These people will receive email."), + 'date': fields.datetime('Date'), + 'ref' : fields.reference('Reference', selection=crm._links_get, size=128), + 'ref2' : fields.reference('Reference 2', selection=crm._links_get, size=128), 'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel."), - 'planned_revenue': fields.float('Planned Revenue'), - 'planned_cost': fields.float('Planned Costs'), - 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), - 'probability': fields.float('Probability (%)'), + 'planned_revenue': fields.float('Planned Revenue'), + 'planned_cost': fields.float('Planned Costs'), + 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), + 'probability': fields.float('Probability (%)'), 'categ_id': fields.many2one('crm.case.categ', 'Category', \ domain="[('section_id','=',section_id),\ - ('object_id.model', '=', 'crm.helpdesk')]"), - 'duration': fields.float('Duration', states={'done': [('readonly', True)]}), - 'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True, + ('object_id.model', '=', 'crm.helpdesk')]"), + 'duration': fields.float('Duration', states={'done': [('readonly', True)]}), + 'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True, help='The state is set to \'Draft\', when a case is created.\ \nIf the case is in progress the state is set to \'Open\'.\ \nWhen the case is over, the state is set to \'Done\'.\ \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), } _defaults = { - 'active': lambda *a: 1, - 'user_id': crm.crm_case._get_default_user, - 'partner_id': crm.crm_case._get_default_partner, - 'partner_address_id': crm.crm_case._get_default_partner_address, - 'email_from': crm.crm_case. _get_default_email, - 'state': lambda *a: 'draft', + 'active': lambda *a: 1, + 'user_id': crm.crm_case._get_default_user, + 'partner_id': crm.crm_case._get_default_partner, + 'partner_address_id': crm.crm_case._get_default_partner_address, + 'email_from': crm.crm_case. _get_default_email, + 'state': lambda *a: 'draft', 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'section_id': crm.crm_case. _get_section, - 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c), - 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], + 'section_id': crm.crm_case. _get_section, + 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c), + 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], } - def message_new(self, cr, uid, msg, context=None): - """ - Automatically calls when new email message arrives - - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks - """ - mailgate_pool = self.pool.get('email.server.tools') - - subject = msg.get('subject') or _("No Subject") - body = msg.get('body') - msg_from = msg.get('from') - priority = msg.get('priority') - + def message_new(self, cr, uid, msg_dict, custom_values=None, context=None): + """Automatically called when new email message arrives""" + res_id = super(crm_helpdesk,self).message_new(cr, uid, msg_dict, custom_values=custom_values, context=context) + subject = msg_dict.get('subject') or _("No Subject") + body = msg_dict.get('body_text') + msg_from = msg_dict.get('from') vals = { 'name': subject, 'email_from': msg_from, - 'email_cc': msg.get('cc'), + 'email_cc': msg_dict.get('cc'), 'description': body, 'user_id': False, } - if msg.get('priority', False): - vals['priority'] = priority - - res = mailgate_pool.get_partner(cr, uid, msg.get('from') or msg.get_unixfrom()) - if res: - vals.update(res) - - res = self.create(cr, uid, vals, context) - attachents = msg.get('attachments', []) - for attactment in attachents or []: - data_attach = { - 'name': attactment, - 'datas':binascii.b2a_base64(str(attachents.get(attactment))), - 'datas_fname': attactment, - 'description': 'Mail attachment', - 'res_model': self._name, - 'res_id': res, - } - self.pool.get('ir.attachment').create(cr, uid, data_attach) + vals.update(self.message_partner_by_email(cr, uid, msg_from)) + self.write(cr, uid, [res_id], vals, context) + return res_id - return res - - def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context=None): - """ - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of update mail’s IDs - """ + def message_update(self, cr, uid, ids, msg, vals={}, default_act='pending', context=None): if isinstance(ids, (str, int, long)): ids = [ids] + super(crm_helpdesk,self).message_update(cr, uid, ids, msg, context=context) + if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): vals['priority'] = msg.get('priority') @@ -156,7 +127,7 @@ class crm_helpdesk(crm.crm_case, osv.osv): 'probability':'probability' } vls = {} - for line in msg['body'].split('\n'): + for line in msg['body_text'].split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower()): @@ -174,19 +145,5 @@ class crm_helpdesk(crm.crm_case, osv.osv): res = self.write(cr, uid, [case.id], values, context=context) return res - def msg_send(self, cr, uid, id, *args, **argv): - - """ Send The Message - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of email’s IDs - @param *args: Return Tuple Value - @param **args: Return Dictionary of Keyword Value - """ - return True - -crm_helpdesk() - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm_helpdesk/crm_helpdesk_view.xml b/addons/crm_helpdesk/crm_helpdesk_view.xml index af893a3f40f1d052f73b98981078f358150cab33..0bdaf256a27df482b1892eca2305fdafab560898 100644 --- a/addons/crm_helpdesk/crm_helpdesk_view.xml +++ b/addons/crm_helpdesk/crm_helpdesk_view.xml @@ -99,12 +99,12 @@ <field name="message_ids" colspan="4" nolabel="1" mode="tree,form" readonly="1"> <tree string="History"> <field name="display_text" string="History Information"/> - <field name="history" invisible="1"/> - <button - string="Reply" attrs="{'invisible': [('history', '!=', True)]}" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.helpdesk', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <field name="email_from" invisible="1"/> + <button + string="Reply" attrs="{'invisible': [('email_from', '=', False)]}" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action" /> </tree> <form string="History"> <group col="4" colspan="4"> @@ -112,20 +112,18 @@ <field name="date"/> <field name="email_to" widget="char" size="512"/> <field name="email_cc" widget="char" size="512"/> - <field name="name" colspan="4" widget="char" size="512"/> - <field name="history" invisible="1"/> + <field name="subject" colspan="4" widget="char" size="512"/> </group> <notebook colspan="4"> <page string="Details"> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <field name="description" colspan="4" nolabel="1" height="250"/> - <button colspan="4" - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'crm.helpdesk', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1" height="250"/> + <button colspan="4" string="Reply" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action"/> </group> - <group attrs="{'invisible': [('history', '=', True)]}"> + <group attrs="{'invisible': [('email_from', '!=', False)]}"> <field name="display_text" colspan="4" nolabel="1" height="250"/> </group> </page> @@ -139,10 +137,9 @@ name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action" /> - <button string="Send New Email" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'crm.helpdesk'}" - icon="terp-mail-message-new" type="action" /> + <button string="Send New Email" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action"/> </page> <page string="Extra Info" groups="base.group_extended"> <group colspan="2" col="2"> diff --git a/addons/crm_helpdesk/report/crm_helpdesk_report.py b/addons/crm_helpdesk/report/crm_helpdesk_report.py index 7b70b77d5d13e2f5078a6b6da25dab076ccdc864..80fc07db38d6f979d8b36758e5335e30efd4d27a 100644 --- a/addons/crm_helpdesk/report/crm_helpdesk_report.py +++ b/addons/crm_helpdesk/report/crm_helpdesk_report.py @@ -56,7 +56,7 @@ class crm_helpdesk_report(osv.osv): 'date_deadline': fields.date('Deadline', select=True), 'priority': fields.selection([('5', 'Lowest'), ('4', 'Low'), \ ('3', 'Normal'), ('2', 'High'), ('1', 'Highest')], 'Priority'), - 'channel_id': fields.many2one('crm.case.channel', 'Channel'), + 'channel_id': fields.many2one('crm.case.channel', 'Channel'), 'categ_id': fields.many2one('crm.case.categ', 'Category', \ domain="[('section_id','=',section_id),\ ('object_id.model', '=', 'crm.helpdesk')]"), @@ -97,7 +97,7 @@ class crm_helpdesk_report(osv.osv): c.planned_cost, count(*) as nbr, extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close, - (SELECT count(id) FROM mailgate_message WHERE model='crm.helpdesk' AND res_id=c.id AND history=True) AS email, + (SELECT count(id) FROM mail_message WHERE model='crm.helpdesk' AND res_id=c.id AND email_from IS NOT NULL) AS email, abs(avg(extract('epoch' from (c.date_deadline - c.date_closed)))/(3600*24)) as delay_expected from crm_helpdesk c diff --git a/addons/crm_partner_assign/crm_lead_view.xml b/addons/crm_partner_assign/crm_lead_view.xml index 60c3d07fedd69cc1ef7f062c645716bffaeef8d4..2ec3c85353bcc85decca42e8d868d933b6a93ae3 100644 --- a/addons/crm_partner_assign/crm_lead_view.xml +++ b/addons/crm_partner_assign/crm_lead_view.xml @@ -61,7 +61,7 @@ <filter string="Referred Partner" icon="terp-personal" domain="[]" context="{'group_by':'partner_assigned_id'}"/> </filter> - <field name="section_id" position="after"> + <field name="partner_id" position="after"> <separator orientation="vertical"/> <field name="partner_assigned_id"/> </field> diff --git a/addons/crm_partner_assign/test/test_crm_partner_assign.yml b/addons/crm_partner_assign/test/test_crm_partner_assign.yml index 1253d55391cb43ae3ac200d3205cb6e019314da6..4d982daab20b17fb293ab86a5b5de062cf8a7905 100644 --- a/addons/crm_partner_assign/test/test_crm_partner_assign.yml +++ b/addons/crm_partner_assign/test/test_crm_partner_assign.yml @@ -1,43 +1,43 @@ - In order to test Forward Partner functionality, I create an opportunity and forward it to partner. -- +- I assign an email address to Administrator. -- +- !record {model: res.users, id: base.user_root}: user_email: admin@openerp.com -- +- I create some partner grades. - I create a grade 'First'. -- +- !record {model: res.partner.grade, id: res_partner_grade_first0}: name: First sequence: 1 -- +- I create another grade 'Second'. -- +- !record {model: res.partner.grade, id: res_partner_grade_second0}: name: Second sequence: 2 -- +- I create one more grade 'Third'. -- +- !record {model: res.partner.grade, id: res_partner_grade_third0}: name: Third sequence: 3 -- +- I assign grade 'First' to the partner 'Axelor'. -- +- !record {model: res.partner, id: base.res_partner_desertic_hispafuentes}: grade_id: res_partner_grade_first0 - I assgin a reply-to email address to Sales Team. -- +- !record {model: crm.case.section, id: crm.section_sales_department}: reply_to: sales_openerp@openerp.com -- +- I create an opportunity 'Questionnaire on OpenERP'. -- +- !record {model: crm.lead, id: crm_lead_questionnaireonopenerp0}: categ_id: crm.categ_oppor7 section_id: crm.section_sales_department @@ -62,7 +62,7 @@ !python {model: crm.lead.forward.to.partner}: | from tools import config vals = { - 'name': 'email', + 'subject': 'email', 'email_to': 'info@axelor.com', 'email_from': 'Administrator <admin@openerp.com>', 'reply_to': 'sales_openerp@openerp.com' diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index 544dbd1ee7bac4e7ce206dc168d2e4aea4263da0..cdaf8f19b127a16325cccac7302b07cbe7d2368f 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -28,10 +28,10 @@ from tools.translate import _ class crm_lead_forward_to_partner(osv.osv_memory): """Forwards lead history""" _name = 'crm.lead.forward.to.partner' - _inherit = "crm.send.mail" + _inherit = "mail.compose.message" _columns = { - 'name': fields.selection([('user', 'User'), ('partner', 'Partner'), \ + 'send_to': fields.selection([('user', 'User'), ('partner', 'Partner'), \ ('email', 'Email Address')], 'Send to', required=True), 'user_id': fields.many2one('res.users', "User"), 'partner_id' : fields.many2one('res.partner', 'Partner'), @@ -40,7 +40,7 @@ class crm_lead_forward_to_partner(osv.osv_memory): } _defaults = { - 'name' : 'email', + 'send_to' : 'email', 'history': 'latest', 'email_from': lambda self, cr, uid, *a: self.pool.get('res.users')._get_email_from(cr, uid, uid)[uid], } @@ -68,13 +68,13 @@ class crm_lead_forward_to_partner(osv.osv_memory): @param hist_id: Id of latest history @param context: A standard dictionary for contextual values """ - log_pool = self.pool.get('mailgate.message') + log_pool = self.pool.get('mail.message') hist = log_pool.browse(cr, uid, hist_id, context=context) header = '-------- Original Message --------' sender = 'From: %s' %(hist.email_from or '') to = 'To: %s' % (hist.email_to or '') sentdate = 'Date: %s' % (hist.date or '') - desc = '\n%s'%(hist.description) + desc = '\n%s'%(hist.body_text) original = [header, sender, to, sentdate, desc] original = '\n'.join(original) return original @@ -109,7 +109,7 @@ class crm_lead_forward_to_partner(osv.osv_memory): res_id = context.get('active_id') msg_val = self._get_case_history(cr, uid, history_type, res_id, context=context) if msg_val: - res = {'value': {'body' : '\n\n' + msg_val}} + res = {'value': {'body_text' : '\n\n' + msg_val}} return res def _get_case_history(self, cr, uid, history_type, res_id, context=None): @@ -125,12 +125,12 @@ class crm_lead_forward_to_partner(osv.osv_memory): elif history_type == 'whole': log_ids = model_pool.browse(cr, uid, res_id, context=context).message_ids - log_ids = map(lambda x: x.id, filter(lambda x: x.history, log_ids)) + log_ids = map(lambda x: x.id, filter(lambda x: x.email_from, log_ids)) msg_val = case_info + '\n\n' + self.get_whole_history(cr, uid, log_ids, context=context) elif history_type == 'latest': log_ids = model_pool.browse(cr, uid, res_id, context=context).message_ids - log_ids = filter(lambda x: x.history and x.id, log_ids) + log_ids = filter(lambda x: x.email_from and x.id, log_ids) if not log_ids: msg_val = case_info else: @@ -170,6 +170,13 @@ class crm_lead_forward_to_partner(osv.osv_memory): email = self.pool.get('res.partner.address').browse(cr, uid, address_id).email return {'value': {'email_to' : email}} + def send_mail(self, cr, uid, ids, context=None): + if context is None: + context = {} + super(crm_lead_forward_to_partner, self).send_mail(cr, uid, ids, context=context) + self.action_forward(cr, uid, ids, context) + return {'type': 'ir.actions.act_window_close'} + def action_forward(self, cr, uid, ids, context=None): """ Forward the lead to a partner @@ -180,15 +187,13 @@ class crm_lead_forward_to_partner(osv.osv_memory): case_pool = self.pool.get(context.get('active_model')) res_id = context and context.get('active_id', False) or False case = case_pool.browse(cr, uid, res_id, context=context) - context.update({'mail': 'forward'}) - super(crm_lead_forward_to_partner, self).action_send(cr, uid, ids, context=context) to_write = {'date_assign': time.strftime('%Y-%m-%d')} - if (this.name == 'partner' and this.partner_id): + if (this.send_to == 'partner' and this.partner_id): to_write['partner_assigned_id'] = this.partner_id.id - if this.name == 'user': + if this.send_to == 'user': to_write.update({'user_id' : this.user_id.id}) email_re = r'([^ ,<@]+@[^> ,]+)' email_cc = re.findall(email_re, case.email_cc or '') @@ -202,7 +207,6 @@ class crm_lead_forward_to_partner(osv.osv_memory): new_cc.append(to) to_write.update({'email_cc' : ', '.join(new_cc) }) case_pool.write(cr, uid, case.id, to_write, context=context) - return {'type': 'ir.actions.act_window_close'} def get_lead_details(self, cr, uid, lead_id, context=None): @@ -285,14 +289,12 @@ class crm_lead_forward_to_partner(osv.osv_memory): body = self._get_case_history(cr, uid, defaults.get('history', 'latest'), lead.id, context=context) defaults.update({ 'subject' : '%s: %s - %s' % (_('Fwd'), 'Openerp lead forward', lead.name), - 'body' : body, + 'body_text' : body, 'email_cc' : email_cc, 'email_to' : email or 'dummy@dummy.ly' }) return defaults -crm_lead_forward_to_partner() - class crm_lead_mass_forward_to_partner(osv.osv_memory): _name = 'crm.lead.mass.forward.to.partner' _inherit = 'crm.lead.forward.to.partner' @@ -312,13 +314,11 @@ class crm_lead_mass_forward_to_partner(osv.osv_memory): continue context.update({'active_id' : case.id}) - value = self.default_get(cr, uid, ['body', 'email_to', 'email_cc', 'subject', 'history'], context=context) + value = self.default_get(cr, uid, ['body_text', 'email_to', 'email_cc', 'subject', 'history'], context=context) self.write(cr, uid, ids, value, context=context) self.action_forward(cr,uid, ids, context=context) return {'type': 'ir.actions.act_window_close'} -crm_lead_mass_forward_to_partner() - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml index 9826bf9d45ca4f6d6cac22ff77757539417e3682..d3ecddb1aa385a0bfd561ac64d70a8d58e19c5f2 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml @@ -1,41 +1,54 @@ <?xml version="1.0" encoding="UTF-8"?> <openerp> <data> + + <record model="ir.ui.view" id="crm_lead_forward_to_partner_form"> <field name="name">crm_lead_forward_to_partner</field> <field name="model">crm.lead.forward.to.partner</field> <field name="type">form</field> - <field name="inherit_id" ref="crm.crm_send_new_mail_view"/> <field name="arch" type="xml"> - <field name="email_from" position="before"> + <form string="Send Mail"> <separator string="Forward to Partner" colspan="4" /> <group col="4" colspan="6"> <field name="history" colspan="2" on_change="on_change_history(history, context)"/> - <field name="name" colspan="2" /> - <group col="2" colspan="2" attrs="{ 'invisible' : [('name','!=','user')]}"> + <field name="send_to" colspan="2" /> + <group col="2" colspan="2" attrs="{ 'invisible' : [('send_to','!=','user')]}"> <field name="user_id" - attrs="{ 'required' : [('name','=','user')]}" + attrs="{ 'required' : [('send_to','=','user')]}" on_change="on_change_email(user_id)" /> </group> - <group col="4" colspan="4" attrs="{'invisible' : [('name','!=','partner')]}"> - <field name="partner_id" attrs="{'required' : [('name','=','partner')]}" on_change="on_change_partner(partner_id)" colspan="2" /> + <group col="4" colspan="4" attrs="{'invisible' : [('send_to','!=','partner')]}"> + <field name="partner_id" attrs="{'required' : [('send_to','=','partner')]}" on_change="on_change_partner(partner_id)" colspan="2" /> <field name="address_id" string="Contact" on_change="on_change_address(address_id)" colspan="2" /> </group> </group> <separator string="" colspan="4" /> - </field> - </field> - </record> - - <record model="ir.ui.view" id="crm_lead_forward_to_partner_form1"> - <field name="name">crm_lead_forward_to_partner1</field> - <field name="model">crm.lead.forward.to.partner</field> - <field name="type">form</field> - <field name="inherit_id" ref="crm.crm_send_new_mail_view"/> - <field name="arch" type="xml"> - <button name="action_send" position="replace"> - <button name="action_forward" string="Forward" icon="gtk-go-forward" type="object" /> - </button> + <group col="6" colspan="4"> + <field name="smtp_server_id" widget="selection" colspan="4"/> + <field name="email_from" colspan="4" required="1"/> + <field name="email_to" colspan="4" required="1"/> + <field name="email_cc" colspan="4"/> + <field name="email_bcc" colspan="4"/> + <field name="reply_to" colspan="4"/> + <field name="subject" colspan="4" widget="char" size="512"/> + </group> + <separator string="" colspan="4"/> + <notebook colspan="4"> + <page string="Body"> + <field name="body" colspan="4" nolabel="1"/> + </page> + <page string="Attachments"> + <label string="Add here all attachments of the current document you want to include in the Email." colspan="4"/> + <field name="attachment_ids" colspan="4" nolabel="1"/> + </page> + </notebook> + <group col="4" colspan="4"> + <label string="" colspan="1"/> + <button icon="gtk-close" special="cancel" string="Close"/> + <button icon="gtk-ok" name="send_mail" string="Send" type="object"/> + </group> + </form> </field> </record> diff --git a/addons/decimal_precision/i18n/en_GB.po b/addons/decimal_precision/i18n/en_GB.po index 869af070de645aeefb34cb1ca3d6127be2ef46da..6cc698e7e43b1f1f669580c3178819976d3b7fee 100644 --- a/addons/decimal_precision/i18n/en_GB.po +++ b/addons/decimal_precision/i18n/en_GB.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-01-11 11:15+0000\n" -"PO-Revision-Date: 2011-08-25 17:35+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"PO-Revision-Date: 2011-09-13 09:03+0000\n" +"Last-Translator: John Bradshaw <Unknown>\n" "Language-Team: English (United Kingdom) <en_GB@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: 2011-09-05 05:47+0000\n" -"X-Generator: Launchpad (build 13830)\n" +"X-Launchpad-Export-Date: 2011-09-14 04:40+0000\n" +"X-Generator: Launchpad (build 13921)\n" #. module: decimal_precision #: field:decimal.precision,digits:0 @@ -31,7 +31,7 @@ msgstr "Decimal Precision" #: model:ir.actions.act_window,name:decimal_precision.action_decimal_precision_form #: model:ir.ui.menu,name:decimal_precision.menu_decimal_precision_form msgid "Decimal Accuracy Definitions" -msgstr "" +msgstr "Decimal Accuracy Definitions" #. module: decimal_precision #: model:ir.module.module,description:decimal_precision.module_meta_information diff --git a/addons/delivery/delivery.py b/addons/delivery/delivery.py index d70af4b4d1e5b60dd700ea755531a9d94d6e81a6..55199a9aa19b8e6838b6e5ba1f7f9e124354036f 100644 --- a/addons/delivery/delivery.py +++ b/addons/delivery/delivery.py @@ -71,7 +71,7 @@ class delivery_carrier(osv.osv): 'free_if_more_than': fields.boolean('Free If More Than'), 'amount': fields.float('Amount'), 'use_detailed_pricelist': fields.boolean('Use Detailed Pricelist'), - 'pricelist_ids': fields.one2many('delivery.grid', 'carrier_id', 'Price List'), + 'pricelist_ids': fields.one2many('delivery.grid', 'carrier_id', 'Advanced Pricing'), } _defaults = { diff --git a/addons/delivery/delivery_demo.xml b/addons/delivery/delivery_demo.xml index 4dfef0c105b972f0aa039652184b446ca07c2573..94666efb26c7e181a0db21a932f7b751e815c79d 100644 --- a/addons/delivery/delivery_demo.xml +++ b/addons/delivery/delivery_demo.xml @@ -17,6 +17,7 @@ <record id="delivery_product" model="product.product"> <field name="name">Delivery by Poste</field> + <field name="default_code">Delivery</field> <field name="type">service</field> <field model="product.category" name="categ_id" search="[]"/> </record> diff --git a/addons/delivery/delivery_view.xml b/addons/delivery/delivery_view.xml index 72f811632ce79f1b15e8695055f799fd119944a6..4ade9b321a7b91b1af358f097f3dd5f7f4db13be 100644 --- a/addons/delivery/delivery_view.xml +++ b/addons/delivery/delivery_view.xml @@ -9,7 +9,6 @@ <field name="type">tree</field> <field name="arch" type="xml"> <tree string="Carrier"> - <field name="name"/> <field name="partner_id"/> <field name="price" invisible="'order_id' not in context"/> </tree> @@ -22,10 +21,9 @@ <field name="arch" type="xml"> <form string="Carrier"> <group colspan="4" col="4" name="general"> - <field name="name" select="1"/> - <field name="active" select="1"/> <field name="partner_id" select="1"/> <field name="product_id" select="1"/> + <field name="active" select="1"/> <separator string="Pricing Information" colspan="6"/> <group colspan="2" col="4"> <field name="normal_price" select="1" colspan="4"/> @@ -82,7 +80,7 @@ <field name="help">Define the delivery methods you are using and their pricing in order to reinvoice the delivery costs when you are doing invoicing based on delivery orders</field> </record> - <menuitem action="action_delivery_carrier_form" id="menu_action_delivery_carrier_form" parent="menu_delivery" groups="base.group_extended"/> + <menuitem action="action_delivery_carrier_form" id="menu_action_delivery_carrier_form" parent="menu_delivery"/> <!-- Delivery Grids --> <record id="view_delivery_grid_tree" model="ir.ui.view"> @@ -132,7 +130,7 @@ <field name="view_mode">tree,form</field> <field name="help">The delivery price list allows you to compute the cost and sales price of the delivery according to the weight of the products and other criteria. You can define several price lists for one delivery method, per country or a zone in a specific country defined by a postal code range.</field> </record> - <menuitem action="action_delivery_grid_form" id="menu_action_delivery_grid_form" parent="menu_delivery"/> + <menuitem action="action_delivery_grid_form" id="menu_action_delivery_grid_form" parent="menu_delivery" groups="base.group_extended"/> <record id="view_delivery_grid_line_form" model="ir.ui.view"> <field name="name">delivery.grid.line.form</field> diff --git a/addons/document/document.py b/addons/document/document.py index 69e123b87ebc7aa7a41d1f75f5342f07039e5ff2..ef9ca01bd1e945a50670c05f9cf7decbb8d82ebb 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -252,7 +252,7 @@ class document_file(osv.osv): ids = ids2 if 'file_size' in vals: # only write that field using direct SQL calls del vals['file_size'] - if len(ids) and len(vals): + if ids and vals: result = super(document_file,self).write(cr, uid, ids, vals, context=context) cr.commit() # ? return result diff --git a/addons/email_template/__init__.py b/addons/email_template/__init__.py index dd87232679d4e2a064f83661365c7bc5b9e8aead..97931ea8bd5fa96b173c6bb0fe61265719f8698f 100644 --- a/addons/email_template/__init__.py +++ b/addons/email_template/__init__.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2009 Sharoon Thomas -# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>) +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,9 +19,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/> # ############################################################################## - -import email_template_account import email_template -import email_template_mailbox import wizard +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/__openerp__.py b/addons/email_template/__openerp__.py index f56062f37ed6293a51ec4eb3bb8ddee6e8163108..e5f83f9ac0c8a0664207a0beb20e0a61979e5827 100644 --- a/addons/email_template/__openerp__.py +++ b/addons/email_template/__openerp__.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2009 Sharoon Thomas -# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>) +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,32 +21,47 @@ ############################################################################## { - "name" : "Email Template for OpenERP", - "version" : "0.7 RC", + "name" : "Email Templates", + "version" : "1.1", "author" : "Openlabs", "website" : "http://openerp.com", "category" : "Tools", 'complexity': "expert", - "depends" : ['marketing', 'base_tools'], + "depends" : ['mail'], "description": """ -Email Template is extraction of Power Email basically just to send emails. -========================================================================== +Email Templating (simplified version of the original Power Email by Openlabs) +============================================================================= -You can define email accounts(server, port, mail format - HTML/Text/Both) -and email templates (resource, recipient, subject, body, attachments). +Lets you design complete email templates related to any OpenERP document (Sale +Orders, Invoices and so on), including sender, recipient, subject, body (HTML and +Text). You may also automatically attach files to your templates, or print and +attach a report. + +For advanced use, the templates may include dynamic attributes of the document +they are related to. For example, you may use the name of a Partner's country +when writing to them, also providing a safe default in case the attribute is +not defined. Each template contains a built-in assistant to help with the +inclusion of these dynamic values. + +If you enable the option, a composition assistant will also appear in the sidebar +of the OpenERP documents to which the template applies (e.g. Invoices). +This serves as a quick way to send a new email based on the template, after +reviewing and adapting the contents, if needed. +This composition assistant will also turn into a mass mailing system when called +for multiple documents at once. + +These email templates are also at the heart of the marketing campaign system +(see the ``marketing_campaign`` application), if you need to automate larger +campaigns on any OpenERP document. + +Technical note: only the templating system of the original Power Email by +Openlabs was kept -For each email template, you can have OpenERP generate a Wizard Action / Button -that will be related to the object. So if you choose to do marketing campaigns -for leads, the action will be added to the right side panel of the Lead form. """, - "init_xml": ['email_template_scheduler_data.xml'], - "update_xml": [ - 'security/email_template_security.xml', - 'email_template_workflow.xml', - 'email_template_account_view.xml', + "data": [ + 'wizard/email_template_preview_view.xml', 'email_template_view.xml', - 'email_template_mailbox_view.xml', - 'wizard/email_template_send_wizard_view.xml', + 'wizard/email_compose_message_view.xml', 'security/ir.model.access.csv' ], "installable": True, diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index aabc211df11056d957c6e1af5ac81041007966cf..3917b3f2ddbb537fc43d9c6ef1389ec89deaacbc 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2009 Sharoon Thomas -# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>) +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,277 +21,186 @@ ############################################################################## import base64 -import random -import netsvc import logging -import re - -TEMPLATE_ENGINES = [] -from osv import osv, fields +import netsvc +from osv import osv +from osv import fields +import tools from tools.translate import _ try: from mako.template import Template as MakoTemplate - TEMPLATE_ENGINES.append(('mako', 'Mako Templates')) except ImportError: - logging.getLogger('init').warning("module email_template: Mako templates not installed") - -try: - from django.template import Context, Template as DjangoTemplate - #Workaround for bug: - #http://code.google.com/p/django-tagging/issues/detail?id=110 - from django.conf import settings - settings.configure() - #Workaround ends - TEMPLATE_ENGINES.append(('django', 'Django Template')) -except ImportError: - logging.getLogger('init').warning("module email_template: Django templates not installed") - -import tools -import pooler -import logging + logging.getLogger('init').warning("email_template: mako templates not available, templating features will not work!") -def get_value(cursor, user, recid, message=None, template=None, context=None): - """ - Evaluates an expression and returns its value - @param cursor: Database Cursor - @param user: ID of current user - @param recid: ID of the target record under evaluation - @param message: The expression to be evaluated - @param template: BrowseRecord object of the current template - @param context: OpenERP Context - @return: Computed message (unicode) or u"" - """ - pool = pooler.get_pool(cursor.dbname) - if message is None: - message = {} - #Returns the computed expression - if message: +class email_template(osv.osv): + "Templates for sending email" + _inherit = 'mail.message' + _name = "email.template" + _description = 'Email Templates' + _rec_name = 'name' # override mail.message's behavior + + def render_template(self, cr, uid, template, model, res_id, context=None): + """Render the given template text, replace mako expressions ``${expr}`` + with the result of evaluating these expressions with + an evaluation context containing: + + * ``user``: browse_record of the current user + * ``object``: browse_record of the document record this mail is + related to + * ``context``: the context passed to the mail composition wizard + + :param str template: the template text to render + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. + """ + if not template: return u"" try: - message = tools.ustr(message) - object = pool.get(template.model_int_name).browse(cursor, user, recid, context=context) - env = { - 'user':pool.get('res.users').browse(cursor, user, user, context=context), - 'db':cursor.dbname - } - if template.template_language == 'mako': - templ = MakoTemplate(message, input_encoding='utf-8') - reply = MakoTemplate(message).render_unicode(object=object, - peobject=object, - env=env, - format_exceptions=True) - elif template.template_language == 'django': - templ = DjangoTemplate(message) - env['object'] = object - env['peobject'] = object - reply = templ.render(Context(env)) - return reply or False + template = tools.ustr(template) + record = None + if res_id: + record = self.pool.get(model).browse(cr, uid, res_id, context=context) + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + result = MakoTemplate(template).render_unicode(object=record, + user=user, + # context kw would clash with mako internals + ctx=context, + format_exceptions=True) + if result == u'False': + result = u'' + return result except Exception: - logging.exception("can't render %r", message) + logging.exception("failed to render mako template value %r", template) return u"" - else: - return message -class email_template(osv.osv): - "Templates for sending Email" - - _name = "email.template" - _description = 'Email Templates for Models' - - def change_model(self, cursor, user, ids, object_name, context=None): - if object_name: - mod_name = self.pool.get('ir.model').read( - cursor, - user, - object_name, - ['model'], context)['model'] + def get_email_template(self, cr, uid, template_id=False, record_id=None, context=None): + if context is None: + context = {} + if not template_id: + return False + template = self.browse(cr, uid, template_id, context) + lang = self.render_template(cr, uid, template.lang, template.model, record_id, context) + if lang: + # Use translated template if necessary + ctx = context.copy() + ctx['lang'] = lang + template = self.browse(cr, uid, template.id, ctx) else: - mod_name = False - return { - 'value':{'model_int_name':mod_name} - } + template = self.browse(cr, uid, int(template_id), context) + return template + + def onchange_model_id(self, cr, uid, ids, model_id, context=None): + mod_name = False + if model_id: + mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model + return {'value':{'model': mod_name}} _columns = { - 'name' : fields.char('Name', size=100, required=True), - 'object_name':fields.many2one('ir.model', 'Resource'), - 'model_int_name':fields.char('Model Internal Name', size=200,), - 'from_account':fields.many2one( - 'email_template.account', - string="Email Account", - help="Emails will be sent from this approved account."), - 'def_to':fields.char( - 'Recipient (To)', - size=250, - help="The Recipient of email. " - "Placeholders can be used here. " - "e.g. ${object.email_to}"), - 'def_cc':fields.char( - 'CC', - size=250, - help="Carbon Copy address(es), comma-separated." - " Placeholders can be used here. " - "e.g. ${object.email_cc}"), - 'def_bcc':fields.char( - 'BCC', - size=250, - help="Blind Carbon Copy address(es), comma-separated." - " Placeholders can be used here. " - "e.g. ${object.email_bcc}"), - 'reply_to':fields.char('Reply-To', - size=250, - help="The address recipients should reply to," - " if different from the From address." - " Placeholders can be used here. " - "e.g. ${object.email_reply_to}"), - 'message_id':fields.char('Message-ID', - size=250, - help="Specify the Message-ID SMTP header to use in outgoing emails. Please note that this overrides the Resource tracking option! Placeholders can be used here."), - 'track_campaign_item':fields.boolean('Resource Tracking', - help="Enable this is you wish to include a special \ -tracking marker in outgoing emails so you can identify replies and link \ -them back to the corresponding resource record. \ -This is useful for CRM leads for example"), - 'lang':fields.char( - 'Language', - size=250, - help="The default language for the email." - " Placeholders can be used here. " - "eg. ${object.partner_id.lang}"), - 'def_subject':fields.char( - 'Subject', - size=200, - help="The subject of email." - " Placeholders can be used here.", - translate=True), - 'def_body_text':fields.text( - 'Standard Body (Text)', - help="The text version of the mail", - translate=True), - 'def_body_html':fields.text( - 'Body (Text-Web Client Only)', - help="The text version of the mail", - translate=True), - 'use_sign':fields.boolean( - 'Signature', - help="the signature from the User details" - " will be appended to the mail"), - 'file_name':fields.char( - 'Report Filename', - size=200, - help="Name of the generated report file. Placeholders can be used in the filename. eg: 2009_SO003.pdf", - translate=True), - 'report_template':fields.many2one( - 'ir.actions.report.xml', - 'Report to send'), - 'attachment_ids': fields.many2many( - 'ir.attachment', - 'email_template_attachment_rel', - 'email_template_id', - 'attachment_id', - 'Attached Files', - help="You may attach existing files to this template, " - "so they will be added in all emails created from this template"), - 'ref_ir_act_window':fields.many2one( - 'ir.actions.act_window', - 'Window Action', - help="Action that will open this email template on Resource records", - readonly=True), - 'ref_ir_value':fields.many2one( - 'ir.values', - 'Wizard Button', - help="Button in the side bar of the form view of this Resource that will invoke the Window Action", - readonly=True), - 'allowed_groups':fields.many2many( - 'res.groups', - 'template_group_rel', - 'templ_id', 'group_id', - string="Allowed User Groups", - help="Only users from these groups will be" - " allowed to send mails from this Template"), - 'model_object_field':fields.many2one( - 'ir.model.fields', - string="Field", - help="Select the field from the model you want to use." - "\nIf it is a relationship field you will be able to " - "choose the nested values in the box below\n(Note:If " - "there are no values make sure you have selected the" - " correct model)", - store=False), - 'sub_object':fields.many2one( - 'ir.model', - 'Sub-model', - help='When a relation field is used this field' - ' will show you the type of field you have selected', - store=False), - 'sub_model_object_field':fields.many2one( - 'ir.model.fields', - 'Sub Field', - help="When you choose relationship fields " - "this field will specify the sub value you can use.", - store=False), - 'null_value':fields.char( - 'Null Value', - help="This Value is used if the field is empty", - size=50, store=False), - 'copyvalue':fields.char( - 'Expression', - size=100, - help="Copy and paste the value in the " - "location you want to use a system value.", - store=False), - 'table_html':fields.text( - 'HTML code', - help="Copy this html code to your HTML message" - " body for displaying the info in your mail.", - store=False), - #Template language(engine eg.Mako) specifics - 'template_language':fields.selection( - TEMPLATE_ENGINES, - 'Templating Language', - required=True - ) + 'name': fields.char('Name', size=250), + 'model_id': fields.many2one('ir.model', 'Related document model'), + 'lang': fields.char('Language Selection', size=250, + help="Optional translation language (ISO code) to select when sending out an email. " + "If not set, the english version will be used. " + "This should usually be a placeholder expression " + "that provides the appropriate language code, e.g. " + "${object.partner_id.lang.code}."), + 'user_signature': fields.boolean('Add Signature', + help="If checked, the user's signature will be appended to the text version " + "of the message"), + 'report_name': fields.char('Report Filename', size=200, translate=True, + help="Name to use for the generated report file (may contain placeholders)\n" + "The extension can be omitted and will then come from the report type."), + 'report_template':fields.many2one('ir.actions.report.xml', 'Optional report to print and attach'), + 'ref_ir_act_window':fields.many2one('ir.actions.act_window', 'Sidebar action', readonly=True, + help="Sidebar action to make this template available on records " + "of the related document model"), + 'ref_ir_value':fields.many2one('ir.values', 'Sidebar button', readonly=True, + help="Sidebar button to open the sidebar action"), + 'track_campaign_item': fields.boolean('Resource Tracking', + help="Enable this is you wish to include a special tracking marker " + "in outgoing emails so you can identify replies and link " + "them back to the corresponding resource record. " + "This is useful for CRM leads for example"), + + # Overridden mail.message.common fields for technical reasons: + 'model': fields.related('model_id','model', type='char', string='Related Document model', + size=128, select=True, store=True, readonly=True), + # we need a separate m2m table to avoid ID collisions with the original mail.message entries + 'attachment_ids': fields.many2many('ir.attachment', 'email_template_attachment_rel', 'email_template_id', + 'attachment_id', 'Files to attach', + help="You may attach files to this template, to be added to all " + "emails created from this template"), + + # Overridden mail.message.common fields to make tooltips more appropriate: + 'subject':fields.char('Subject', size=512, translate=True, help="Subject (placeholders may be used here)",), + 'email_from': fields.char('From', size=128, help="Sender address (placeholders may be used here)"), + 'email_to': fields.char('To', size=256, help="Comma-separated recipient addresses (placeholders may be used here)"), + 'email_cc': fields.char('Cc', size=256, help="Carbon copy recipients (placeholders may be used here)"), + 'email_bcc': fields.char('Bcc', size=256, help="Blind carbon copy recipients (placeholders may be used here)"), + 'reply_to': fields.char('Reply-To', size=250, help="Preferred response address (placeholders may be used here)"), + 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False, + help="Optional preferred server for outgoing mails. If not set, the highest " + "priority one will be used."), + 'body_text': fields.text('Text contents', translate=True, help="Plaintext version of the message (placeholders may be used here)"), + 'body_html': fields.text('Rich-text contents', help="Rich-text/HTML version of the message (placeholders may be used here)"), + 'message_id': fields.char('Message-Id', size=256, help="Message-ID SMTP header to use in outgoing messages based on this template. " + "Please note that this overrides the 'Resource Tracking' option, " + "so if you simply need to track replies to outgoing emails, enable " + "that option instead.\n" + "Placeholders must be used here, as this value always needs to be unique!"), + + # Fake fields used to implement the placeholder assistant + 'model_object_field': fields.many2one('ir.model.fields', string="Field", + help="Select target field from the related document model.\n" + "If it is a relationship field you will be able to select " + "a target field at the destination of the relationship."), + 'sub_object': fields.many2one('ir.model', 'Sub-model', readonly=True, + help="When a relationship field is selected as first field, " + "this field shows the document model the relationship goes to."), + 'sub_model_object_field': fields.many2one('ir.model.fields', 'Sub-field', + help="When a relationship field is selected as first field, " + "this field lets you select the target field within the " + "destination document model (sub-model)."), + 'null_value': fields.char('Null value', help="Optional value to use if the target field is empty", size=128), + 'copyvalue': fields.char('Expression', size=256, help="Final placeholder expression, to be copy-pasted in the desired template field."), } _defaults = { - 'template_language' : lambda *a:'mako', - + 'track_campaign_item': True } - _sql_constraints = [ - ('name', 'unique (name)','The template name must be unique !') - ] - def create_action(self, cr, uid, ids, context=None): vals = {} - if context is None: - context = {} - template_obj = self.browse(cr, uid, ids, context=context)[0] - src_obj = template_obj.object_name.model - vals['ref_ir_act_window'] = self.pool.get('ir.actions.act_window').create(cr, uid, { - 'name': template_obj.name, - 'type': 'ir.actions.act_window', - 'res_model': 'email_template.send.wizard', - 'src_model': src_obj, - 'view_type': 'form', - 'context': "{'src_model':'%s','template_id':'%d','src_rec_id':active_id,'src_rec_ids':active_ids}" % (src_obj, template_obj.id), - 'view_mode':'form,tree', - 'view_id': self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'email_template.send.wizard.form')], context=context)[0], - 'target': 'new', - 'auto_refresh':1 - }, context) - ir_values_obj = self.pool.get('ir.values') - vals['ref_ir_value'] = ir_values_obj.create(cr, uid, { - 'name': _('Send Mail (%s)') % template_obj.name, - 'model': src_obj, - 'key2': 'client_action_multi', - 'value': "ir.actions.act_window," + str(vals['ref_ir_act_window']), - 'object': True, - }, context) + action_obj = self.pool.get('ir.actions.act_window') + data_obj = self.pool.get('ir.model.data') + for template in self.browse(cr, uid, ids, context=context): + src_obj = template.model_id.model + model_data_id = data_obj._get_id(cr, uid, 'mail', 'email_compose_message_wizard_form') + res_id = data_obj.browse(cr, uid, model_data_id, context=context).res_id + vals['ref_ir_act_window'] = action_obj.create(cr, uid, { + 'name': template.name, + 'type': 'ir.actions.act_window', + 'res_model': 'mail.compose.message', + 'src_model': src_obj, + 'view_type': 'form', + 'context': "{'mail.compose.message.mode':'mass_mail'}", + 'view_mode':'form,tree', + 'view_id': res_id, + 'target': 'new', + 'auto_refresh':1 + }, context) + vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, uid, { + 'name': _('Send Mail (%s)') % template.name, + 'model': src_obj, + 'key2': 'client_action_multi', + 'value': "ir.actions.act_window," + str(vals['ref_ir_act_window']), + 'object': True, + }, context) self.write(cr, uid, ids, { - 'ref_ir_act_window': vals['ref_ir_act_window'], - 'ref_ir_value': vals['ref_ir_value'], - }, context) + 'ref_ir_act_window': vals.get('ref_ir_act_window',False), + 'ref_ir_value': vals.get('ref_ir_value',False), + }, context) return True def unlink_action(self, cr, uid, ids, context=None): @@ -304,9 +213,6 @@ This is useful for CRM leads for example"), ir_values_obj.unlink(cr, uid, template.ref_ir_value.id, context) except: raise osv.except_osv(_("Warning"), _("Deletion of Record failed")) - - def delete_action(self, cr, uid, ids, context=None): - self.unlink_action(cr, uid, ids, context=context) return True def unlink(self, cr, uid, ids, context=None): @@ -314,511 +220,167 @@ This is useful for CRM leads for example"), return super(email_template, self).unlink(cr, uid, ids, context=context) def copy(self, cr, uid, id, default=None, context=None): + template = self.browse(cr, uid, id, context=context) if default is None: default = {} default = default.copy() - old = self.read(cr, uid, id, ['name'], context=context) - new_name = _("Copy of template %s") % old.get('name', 'No Name') - check = self.search(cr, uid, [('name', '=', new_name)], context=context) - if check: - new_name = new_name + '_' + random.choice('abcdefghij') + random.choice('lmnopqrs') + random.choice('tuvwzyz') - default.update({'name':new_name}) + default['name'] = template.name + _('(copy)') return super(email_template, self).copy(cr, uid, id, default, context) - def build_expression(self, field_name, sub_field_name, null_value, template_language='mako'): - """ - Returns a template expression based on data provided - @param field_name: field name - @param sub_field_name: sub field name (M2O) - @param null_value: default value if the target value is empty - @param template_language: name of template engine - @return: computed expression - """ + def build_expression(self, field_name, sub_field_name, null_value): + """Returns a placeholder expression for use in a template field, + based on the values provided in the placeholder assistant. + :param field_name: main field name + :param sub_field_name: sub field name (M2O) + :param null_value: default value if the target value is empty + :return: final placeholder expression + """ expression = '' - if template_language == 'mako': - if field_name: - expression = "${object." + field_name - if sub_field_name: - expression += "." + sub_field_name - if null_value: - expression += " or '''%s'''" % null_value - expression += "}" - elif template_language == 'django': - if field_name: - expression = "{{object." + field_name - if sub_field_name: - expression += "." + sub_field_name - if null_value: - expression += "|default: '''%s'''" % null_value - expression += "}}" + if field_name: + expression = "${object." + field_name + if sub_field_name: + expression += "." + sub_field_name + if null_value: + expression += " or '''%s'''" % null_value + expression += "}" return expression - def onchange_model_object_field(self, cr, uid, ids, model_object_field, template_language, context=None): - if not model_object_field: - return {} - result = {} - field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) - #Check if field is relational - if field_obj.ttype in ['many2one', 'one2many', 'many2many']: - res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) - if res_ids: - result['sub_object'] = res_ids[0] - result['copyvalue'] = self.build_expression(False, - False, - False, - template_language) - result['sub_model_object_field'] = False - result['null_value'] = False - else: - #Its a simple field... just compute placeholder - result['sub_object'] = False - result['copyvalue'] = self.build_expression(field_obj.name, - False, - False, - template_language - ) - result['sub_model_object_field'] = False - result['null_value'] = False + def onchange_sub_model_object_value_field(self, cr, uid, ids, model_object_field, sub_model_object_field=False, null_value=None, context=None): + result = { + 'sub_object': False, + 'copyvalue': False, + 'sub_model_object_field': False, + 'null_value': False + } + if model_object_field: + fields_obj = self.pool.get('ir.model.fields') + field_value = fields_obj.browse(cr, uid, model_object_field, context) + if field_value.ttype in ['many2one', 'one2many', 'many2many']: + res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_value.relation)], context=context) + sub_field_value = False + if sub_model_object_field: + sub_field_value = fields_obj.browse(cr, uid, sub_model_object_field, context) + if res_ids: + result.update({ + 'sub_object': res_ids[0], + 'copyvalue': self.build_expression(field_value.name, sub_field_value and sub_field_value.name or False, null_value or False), + 'sub_model_object_field': sub_model_object_field or False, + 'null_value': null_value or False + }) + else: + result.update({ + 'copyvalue': self.build_expression(field_value.name, False, null_value or False), + 'null_value': null_value or False + }) return {'value':result} - def onchange_sub_model_object_field(self, cr, uid, ids, model_object_field, sub_model_object_field, template_language, context=None): - if not model_object_field or not sub_model_object_field: - return {} - result = {} - field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) - if field_obj.ttype in ['many2one', 'one2many', 'many2many']: - res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) - sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context) - if res_ids: - result['sub_object'] = res_ids[0] - result['copyvalue'] = self.build_expression(field_obj.name, - sub_field_obj.name, - False, - template_language - ) - result['sub_model_object_field'] = sub_model_object_field - result['null_value'] = False - else: - #Its a simple field... just compute placeholder - result['sub_object'] = False - result['copyvalue'] = self.build_expression(field_obj.name, - False, - False, - template_language - ) - result['sub_model_object_field'] = False - result['null_value'] = False - return {'value':result} - def onchange_null_value(self, cr, uid, ids, model_object_field, sub_model_object_field, null_value, template_language, context=None): - if not model_object_field and not null_value: - return {} - result = {} - field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context) - if field_obj.ttype in ['many2one', 'one2many', 'many2many']: - res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context) - sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context) - if res_ids: - result['sub_object'] = res_ids[0] - result['copyvalue'] = self.build_expression(field_obj.name, - sub_field_obj.name, - null_value, - template_language - ) - result['sub_model_object_field'] = sub_model_object_field - result['null_value'] = null_value - else: - #Its a simple field... just compute placeholder - result['sub_object'] = False - result['copyvalue'] = self.build_expression(field_obj.name, - False, - null_value, - template_language - ) - result['sub_model_object_field'] = False - result['null_value'] = null_value - return {'value':result} + def generate_email(self, cr, uid, template_id, res_id, context=None): + """Generates an email from the template for given (model, res_id) pair. - def _add_attachment(self, cursor, user, mailbox_id, name, data, filename, context=None): - """ - Add an attachment to a given mailbox entry. - - :param data: base64 encoded attachment data to store - """ - attachment_obj = self.pool.get('ir.attachment') - attachment_data = { - 'name': (name or '') + _(' (Email Attachment)'), - 'datas': data, - 'datas_fname': filename, - 'description': name or _('No Description'), - 'res_model':'email_template.mailbox', - 'res_id': mailbox_id, - } - attachment_id = attachment_obj.create(cursor, - user, - attachment_data, - context) - if attachment_id: - self.pool.get('email_template.mailbox').write( - cursor, - user, - mailbox_id, - { - 'attachments_ids':[(4, attachment_id)], - 'mail_type':'multipart/mixed' - }, - context) - - def generate_attach_reports(self, - cursor, - user, - template, - record_id, - mail, - context=None): - """ - Generate report to be attached and attach it - to the email, and add any directly attached files as well. - - @param cursor: Database Cursor - @param user: ID of User - @param template: Browse record of - template - @param record_id: ID of the target model - for which this mail has - to be generated - @param mail: Browse record of email object - @return: True - """ - if template.report_template: - reportname = 'report.' + \ - self.pool.get('ir.actions.report.xml').read( - cursor, - user, - template.report_template.id, - ['report_name'], - context)['report_name'] - service = netsvc.LocalService(reportname) - data = {} - data['model'] = template.model_int_name - (result, format) = service.create(cursor, - user, - [record_id], - data, - context) - fname = tools.ustr(get_value(cursor, user, record_id, - template.file_name, template, context) - or 'Report') - ext = '.' + format - if not fname.endswith(ext): - fname += ext - self._add_attachment(cursor, user, mail.id, mail.subject, base64.b64encode(result), fname, context) - - if template.attachment_ids: - for attachment in template.attachment_ids: - self._add_attachment(cursor, user, mail.id, attachment.name, attachment.datas, attachment.datas_fname, context) - - return True - - def _generate_mailbox_item_from_template(self, - cursor, - user, - template, - record_id, - context=None): - """ - Generates an email from the template for - record record_id of target object - - @param cursor: Database Cursor - @param user: ID of User - @param template: Browse record of - template - @param record_id: ID of the target model - for which this mail has - to be generated - @return: ID of created object + :param template_id: id of the template to render. + :param res_id: id of the record to use for rendering the template (model + is taken from template definition) + :returns: a dict containing all relevant fields for creating a new + mail.message entry, with the addition one additional + special key ``attachments`` containing a list of """ if context is None: context = {} - #If account to send from is in context select it, else use enforced account - if 'account_id' in context.keys(): - from_account = self.pool.get('email_template.account').read( - cursor, - user, - context.get('account_id'), - ['name', 'email_id'], - context - ) - else: - from_account = { - 'id':template.from_account.id, - 'name':template.from_account.name, - 'email_id':template.from_account.email_id - } - lang = get_value(cursor, - user, - record_id, - template.lang, - template, - context) - if lang: - ctx = context.copy() - ctx.update({'lang':lang}) - template = self.browse(cursor, user, template.id, context=ctx) - - # determine name of sender, either it is specified in email_id or we - # use the account name - email_id = from_account['email_id'].strip() - email_from = re.findall(r'([^ ,<@]+@[^> ,]+)', email_id)[0] - if email_from != email_id: - # we should keep it all, name is probably specified in the address - email_from = from_account['email_id'] - else: - email_from = tools.ustr(from_account['name']) + "<" + tools.ustr(email_id) + ">" - - # FIXME: should do this in a loop and rename template fields to the corresponding - # mailbox fields. (makes no sense to have different names I think. - mailbox_values = { - 'email_from': email_from, - 'email_to':get_value(cursor, - user, - record_id, - template.def_to, - template, - context), - 'email_cc':get_value(cursor, - user, - record_id, - template.def_cc, - template, - context), - 'email_bcc':get_value(cursor, - user, - record_id, - template.def_bcc, - template, - context), - 'reply_to':get_value(cursor, - user, - record_id, - template.reply_to, - template, - context), - 'subject':get_value(cursor, - user, - record_id, - template.def_subject, - template, - context), - 'body_text':get_value(cursor, - user, - record_id, - template.def_body_text, - template, - context), - 'body_html':get_value(cursor, - user, - record_id, - template.def_body_html, - template, - context), - 'account_id' :from_account['id'], - #This is a mandatory field when automatic emails are sent - 'state':'na', - 'folder':'drafts', - 'mail_type':'multipart/alternative', + values = { + 'subject': False, + 'body_text': False, + 'body_html': False, + 'email_from': False, + 'email_to': False, + 'email_cc': False, + 'email_bcc': False, + 'reply_to': False, + 'auto_delete': False, + 'model': False, + 'res_id': False, + 'mail_server_id': False, + 'attachments': False, + 'attachment_ids': False, + 'message_id': False, + 'state': 'outgoing', } - - if template['message_id']: - # use provided message_id with placeholders - mailbox_values.update({'message_id': get_value(cursor, user, record_id, template['message_id'], template, context)}) - - elif template['track_campaign_item']: - # get appropriate message-id - mailbox_values.update({'message_id': tools.misc.generate_tracking_message_id(record_id)}) - - if not mailbox_values['account_id']: - raise Exception("Unable to send the mail. No account linked to the template.") - #Use signatures if allowed - if template.use_sign: - sign = self.pool.get('res.users').read(cursor, - user, - user, - ['signature'], - context)['signature'] - if mailbox_values['body_text']: - mailbox_values['body_text'] += sign - if mailbox_values['body_html']: - mailbox_values['body_html'] += sign - mailbox_id = self.pool.get('email_template.mailbox').create( - cursor, - user, - mailbox_values, - context) - - return mailbox_id - - - def generate_mail(self, - cursor, - user, - template_id, - record_ids, - context=None): - if context is None: - context = {} - template = self.browse(cursor, user, template_id, context=context) - if not template: - raise Exception("The requested template could not be loaded") - result = True - mailbox_obj = self.pool.get('email_template.mailbox') - for record_id in record_ids: - mailbox_id = self._generate_mailbox_item_from_template( - cursor, - user, - template, - record_id, - context) - mail = mailbox_obj.browse( - cursor, - user, - mailbox_id, - context=context - ) - if template.report_template or template.attachment_ids: - self.generate_attach_reports( - cursor, - user, - template, - record_id, - mail, - context - ) - - self.pool.get('email_template.mailbox').write( - cursor, - user, - mailbox_id, - {'folder':'outbox'}, - context=context - ) - # TODO : manage return value of all the records - result = self.pool.get('email_template.mailbox').send_this_mail(cursor, user, [mailbox_id], context) - return result - -email_template() - - -## FIXME: this class duplicates a lot of features of the email template send wizard, -## one of the 2 should inherit from the other! - -class email_template_preview(osv.osv_memory): - _name = "email_template.preview" - _description = "Email Template Preview" - - def _get_model_recs(self, cr, uid, context=None): - if context is None: - context = {} - #Fills up the selection box which allows records from the selected object to be displayed - self.context = context - if 'template_id' in context: - ref_obj_id = self.pool.get('email.template').read(cr, uid, context['template_id'], ['object_name'], context) - ref_obj_name = self.pool.get('ir.model').read(cr, uid, ref_obj_id['object_name'][0], ['model'], context)['model'] - model_obj = self.pool.get(ref_obj_name) - ref_obj_ids = model_obj.search(cr, uid, [], 0, 20, 'id', context=context) - if not ref_obj_ids: - ref_obj_ids = [] - - # also add the default one if requested, otherwise it won't be available for selection: - default_id = context.get('default_rel_model_ref') - if default_id and default_id not in ref_obj_ids: - ref_obj_ids.insert(0, default_id) - return model_obj.name_get(cr, uid, ref_obj_ids, context) - return [] - - def default_get(self, cr, uid, fields, context=None): - if context is None: - context = {} - result = super(email_template_preview, self).default_get(cr, uid, fields, context=context) - if (not fields or 'rel_model_ref' in fields) and 'template_id' in context \ - and not result.get('rel_model_ref'): - selectables = self._get_model_recs(cr, uid, context=context) - result['rel_model_ref'] = selectables and selectables[0][0] or False - return result - - def _default_model(self, cursor, user, context=None): - """ - Returns the default value for model field - @param cursor: Database Cursor - @param user: ID of current user - @param context: OpenERP Context - """ - return self.pool.get('email.template').read( - cursor, - user, - context['template_id'], - ['object_name'], - context).get('object_name', False) - - _columns = { - 'ref_template':fields.many2one( - 'email.template', - 'Template', readonly=True), - 'rel_model':fields.many2one('ir.model', 'Model', readonly=True), - 'rel_model_ref':fields.selection(_get_model_recs, 'Referred Document'), - 'to':fields.char('To', size=250, readonly=True), - 'cc':fields.char('CC', size=250, readonly=True), - 'bcc':fields.char('BCC', size=250, readonly=True), - 'reply_to':fields.char('Reply-To', - size=250, - help="The address recipients should reply to," - " if different from the From address." - " Placeholders can be used here."), - 'message_id':fields.char('Message-ID', - size=250, - help="The Message-ID header value, if you need to" - "specify it, for example to automatically recognize the replies later." - " Placeholders can be used here."), - 'subject':fields.char('Subject', size=200, readonly=True), - 'body_text':fields.text('Body', readonly=True), - 'body_html':fields.text('Body', readonly=True), - 'report':fields.char('Report Name', size=100, readonly=True), - } - _defaults = { - 'ref_template': lambda self, cr, uid, ctx:ctx['template_id'] or False, - 'rel_model': _default_model, - } - def on_change_ref(self, cr, uid, ids, rel_model_ref, context=None): - if context is None: - context = {} - if not rel_model_ref: - return {} - vals = {} - if context == {}: - context = self.context - template = self.pool.get('email.template').browse(cr, uid, context['template_id'], context) - #Search translated template - lang = get_value(cr, uid, rel_model_ref, template.lang, template, context) - if lang: + if not template_id: + return values + + report_xml_pool = self.pool.get('ir.actions.report.xml') + template = self.get_email_template(cr, uid, template_id, res_id, context) + + for field in ['subject', 'body_text', 'body_html', 'email_from', + 'email_to', 'email_cc', 'email_bcc', 'reply_to', + 'message_id']: + values[field] = self.render_template(cr, uid, getattr(template, field), + template.model, res_id, context=context) \ + or False + + if template.user_signature: + signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature + values['body_text'] += '\n\n' + signature + + values.update(mail_server_id = template.mail_server_id.id or False, + auto_delete = template.auto_delete, + model=template.model, + res_id=res_id or False) + + attachments = {} + # Add report as a Document + if template.report_template: + report_name = template.report_name + report_service = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name + # Ensure report is rendered using template's language ctx = context.copy() - ctx.update({'lang':lang}) - template = self.pool.get('email.template').browse(cr, uid, context['template_id'], ctx) - vals['to'] = get_value(cr, uid, rel_model_ref, template.def_to, template, context) - vals['cc'] = get_value(cr, uid, rel_model_ref, template.def_cc, template, context) - vals['bcc'] = get_value(cr, uid, rel_model_ref, template.def_bcc, template, context) - vals['reply_to'] = get_value(cr, uid, rel_model_ref, template.reply_to, template, context) - if template.message_id: - vals['message_id'] = get_value(cr, uid, rel_model_ref, template.message_id, template, context) - elif template.track_campaign_item: - vals['message_id'] = tools.misc.generate_tracking_message_id(rel_model_ref) - vals['subject'] = get_value(cr, uid, rel_model_ref, template.def_subject, template, context) - vals['body_text'] = get_value(cr, uid, rel_model_ref, template.def_body_text, template, context) - vals['body_html'] = get_value(cr, uid, rel_model_ref, template.def_body_html, template, context) - vals['report'] = get_value(cr, uid, rel_model_ref, template.file_name, template, context) - return {'value':vals} - -email_template_preview() + if template.lang: + ctx['lang'] = self.render_template(cr, uid, template.lang, template.model, res_id, context) + service = netsvc.LocalService(report_service) + (result, format) = service.create(cr, uid, [res_id], {'model': template.model}, ctx) + result = base64.b64encode(result) + if not report_name: + report_name = report_service + ext = "." + format + if not report_name.endswith(ext): + report_name += ext + attachments[report_name] = result + + # Add document attachments + for attach in template.attachment_ids: + # keep the bytes as fetched from the db, base64 encoded + attachments[attach.datas_fname] = attach.datas + + values['attachments'] = attachments + return values + + def send_mail(self, cr, uid, template_id, res_id, context=None): + """Generates a new mail message for the given template and record, + and schedule it for delivery through the ``mail`` module's scheduler. + + :param int template_id: id of the template to render + :param int res_id: id of the record to render the template with + (model is taken from the template) + """ + mail_message = self.pool.get('mail.message') + ir_attachment = self.pool.get('ir.attachment') + template = self.browse(cr, uid, template_id, context) + values = self.generate_email(cr, uid, template_id, res_id, context=context) + attachments = values.pop('attachments') or {} + message_id = mail_message.create(cr, uid, values, context=context) + # link attachments + attachment_ids = [] + for fname, fcontent in attachments.iteritems(): + attachment_data = { + 'name': fname, + 'datas_fname': fname, + 'datas': fcontent, + 'res_model': mail_message._name, + 'res_id': message_id, + } + if context.has_key('default_type'): + del context['default_type'] + attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context)) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/email_template_account.py b/addons/email_template/email_template_account.py deleted file mode 100644 index a5f51400c8a21124236209924dcd337fd586e2cc..0000000000000000000000000000000000000000 --- a/addons/email_template/email_template_account.py +++ /dev/null @@ -1,470 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2009 Sharoon Thomas -# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -############################################################################## - -from osv import osv, fields -import re -import smtplib -import base64 -from email import Encoders -from email.mime.base import MIMEBase -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.header import decode_header, Header -from email.utils import formatdate -import netsvc -import datetime -from tools.translate import _ -import tools -import logging - -EMAIL_PATTERN = re.compile(r'([^()\[\] ,<:\\>@";]+@[^()\[\] ,<:\\>@";]+)') # See RFC822 -def extract_emails(emails_str): - """ - Returns a list of email addresses recognized in a string, ignoring the rest of the string. - extract_emails('a@b.com,c@bcom, "John Doe" <d@b.com> , e@b.com') -> ['a@b.com','c@bcom', 'd@b.com', 'e@b.com']" - """ - return EMAIL_PATTERN.findall(emails_str) - - -def extract_emails_from_dict(addresses={}): - """ - Extracts email addresses from a dictionary with comma-separated address string values, handling - separately the To, CC, BCC and Reply-To addresses. - - :param addresses: a dictionary of addresses in the form {'To': 'a@b.com,c@bcom; d@b.com;e@b.com' , 'CC': 'e@b.com;f@b.com', ... } - :return: a dictionary with a list of separate addresses for each header (To, CC, BCC), with an additional key 'all-recipients' - containing all addresses for the 'To', 'CC', 'BCC' entries. - """ - result = {'all-recipients':[]} - keys = ['To', 'CC', 'BCC', 'Reply-To'] - for each in keys: - emails = extract_emails(addresses.get(each, u'')) - while u'' in emails: - emails.remove(u'') - result[each] = emails - if each != 'Reply-To': - result['all-recipients'].extend(emails) - return result - -class email_template_account(osv.osv): - """ - Object to store email account settings - """ - _name = "email_template.account" - _known_content_types = ['multipart/mixed', - 'multipart/alternative', - 'multipart/related', - 'text/plain', - 'text/html' - ] - _columns = { - 'name': fields.char('Description', - size=64, required=True, - readonly=True, select=True, - help="The description is used as the Sender name along with the provided From Email, \ -unless it is already specified in the From Email, e.g: John Doe <john@doe.com>", - states={'draft':[('readonly', False)]}), - 'auto_delete': fields.boolean('Auto Delete', size=64, readonly=True, - help="Permanently delete emails after sending", - states={'draft':[('readonly', False)]}), - 'user':fields.many2one('res.users', - 'Related User', required=True, - readonly=True, states={'draft':[('readonly', False)]}), - 'email_id': fields.char('From Email', - size=120, required=True, - readonly=True, states={'draft':[('readonly', False)]} , - help="eg: 'john@doe.com' or 'John Doe <john@doe.com>'"), - 'smtpserver': fields.char('Server', - size=120, required=True, - readonly=True, states={'draft':[('readonly', False)]}, - help="Enter name of outgoing server, eg: smtp.yourdomain.com"), - 'smtpport': fields.integer('SMTP Port', - size=64, required=True, - readonly=True, states={'draft':[('readonly', False)]}, - help="Enter port number, eg: 25 or 587"), - 'smtpuname': fields.char('User Name', - size=120, required=False, - readonly=True, states={'draft':[('readonly', False)]}, - help="Specify the username if your SMTP server requires authentication, " - "otherwise leave it empty."), - 'smtppass': fields.char('Password', - size=120, invisible=True, - required=False, readonly=True, - states={'draft':[('readonly', False)]}), - 'smtptls':fields.boolean('TLS', - states={'draft':[('readonly', False)]}, readonly=True), - - 'smtpssl':fields.boolean('SSL/TLS (only in python 2.6)', - states={'draft':[('readonly', False)]}, readonly=True), - 'send_pref':fields.selection([ - ('html', 'HTML, otherwise Text'), - ('text', 'Text, otherwise HTML'), - ('alternative', 'Both HTML & Text (Alternative)'), - ('mixed', 'Both HTML & Text (Mixed)') - ], 'Mail Format', required=True), - 'company':fields.selection([ - ('yes', 'Yes'), - ('no', 'No') - ], 'Corporate', - readonly=True, - help="Select if this mail account does not belong " \ - "to specific user but to the organization as a whole. " \ - "eg: info@companydomain.com", - required=True, states={ - 'draft':[('readonly', False)] - }), - - 'state':fields.selection([ - ('draft', 'Initiated'), - ('suspended', 'Suspended'), - ('approved', 'Approved') - ], - 'State', required=True, readonly=True), - } - - _defaults = { - 'name':lambda self, cursor, user, context:self.pool.get( - 'res.users' - ).read( - cursor, - user, - user, - ['name'], - context - )['name'], - 'state':lambda * a:'draft', - 'smtpport':lambda *a:25, - 'smtpserver':lambda *a:'localhost', - 'company':lambda *a:'yes', - 'user':lambda self, cursor, user, context:user, - 'send_pref':lambda *a: 'html', - 'smtptls':lambda *a:True, - } - - _sql_constraints = [ - ( - 'email_uniq', - 'unique (email_id)', - 'Another setting already exists with this email ID !') - ] - - def name_get(self, cr, uid, ids, context=None): - return [(a["id"], "%s (%s)" % (a['email_id'], a['name'])) for a in self.read(cr, uid, ids, ['name', 'email_id'], context=context)] - - def _constraint_unique(self, cursor, user, ids, context=None): - """ - This makes sure that you dont give personal - users two accounts with same ID (Validated in sql constaints) - However this constraint exempts company accounts. - Any no of co accounts for a user is allowed - """ - if self.read(cursor, user, ids, ['company'])[0]['company'] == 'no': - accounts = self.search(cursor, user, [ - ('user', '=', user), - ('company', '=', 'no') - ]) - if len(accounts) > 1 : - return False - else : - return True - else: - return True - - _constraints = [ - (_constraint_unique, - 'Error: You are not allowed to have more than 1 account.', - []) - ] - - def get_outgoing_server(self, cursor, user, ids, context=None): - """ - Returns the Out Going Connection (SMTP) object - - @attention: DO NOT USE except_osv IN THIS METHOD - @param cursor: Database Cursor - @param user: ID of current user - @param ids: ID/list of ids of current object for - which connection is required - First ID will be chosen from lists - @param context: Context - - @return: SMTP server object or Exception - """ - #Type cast ids to integer - if type(ids) == list: - ids = ids[0] - this_object = self.browse(cursor, user, ids, context=context) - if this_object: - if this_object.smtpserver and this_object.smtpport: - try: - if this_object.smtpssl: - serv = smtplib.SMTP_SSL(this_object.smtpserver, this_object.smtpport) - else: - serv = smtplib.SMTP(this_object.smtpserver, this_object.smtpport) - if this_object.smtptls: - serv.ehlo() - serv.starttls() - serv.ehlo() - except Exception, error: - raise error - try: - if serv.has_extn('AUTH') or this_object.smtpuname or this_object.smtppass: - serv.login(str(this_object.smtpuname), str(this_object.smtppass)) - except Exception, error: - raise error - return serv - raise Exception(_("SMTP SERVER or PORT not specified")) - raise Exception(_("Core connection for the given ID does not exist")) - - def check_outgoing_connection(self, cursor, user, ids, context=None): - """ - checks SMTP credentials and confirms if outgoing connection works - (Attached to button) - @param cursor: Database Cursor - @param user: ID of current user - @param ids: list of ids of current object for - which connection is required - @param context: Context - """ - try: - self.get_outgoing_server(cursor, user, ids, context) - raise osv.except_osv(_("SMTP Test Connection Was Successful"), '') - except osv.except_osv, success_message: - raise success_message - except Exception, error: - raise osv.except_osv( - _("Out going connection test failed"), - _("Reason: %s") % tools.ustr(error) - ) - - def do_approval(self, cr, uid, ids, context=None): - #TODO: Check if user has rights - self.write(cr, uid, ids, {'state':'approved'}, context=context) -# wf_service = netsvc.LocalService("workflow") - - def smtp_connection(self, cursor, user, id, context=None): - """ - This method should now wrap smtp_connection - """ - #This function returns a SMTP server object - logger = netsvc.Logger() - core_obj = self.browse(cursor, user, id, context=context) - if core_obj.smtpserver and core_obj.smtpport and core_obj.state == 'approved': - try: - serv = self.get_outgoing_server(cursor, user, id, context) - except Exception, error: - logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed on login. Probable Reason:Could not login to server\nError: %s") % (id, tools.ustr(error))) - return False - #Everything is complete, now return the connection - return serv - else: - logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id) - return False - -#**************************** MAIL SENDING FEATURES ***********************# - - - - - def send_mail(self, cr, uid, ids, addresses, subject='', body=None, payload=None, message_id=None, context=None): - #TODO: Replace all this with a single email object - if body is None: - body = {} - if payload is None: - payload = {} - if context is None: - context = {} - logger = netsvc.Logger() - for id in ids: - core_obj = self.browse(cr, uid, id, context) - serv = self.smtp_connection(cr, uid, id) - if serv: - try: - # Prepare multipart containers depending on data - text_subtype = (core_obj.send_pref == 'alternative') and 'alternative' or 'mixed' - # Need a multipart/mixed wrapper for attachments if content is alternative - if payload and text_subtype == 'alternative': - payload_part = MIMEMultipart(_subtype='mixed') - text_part = MIMEMultipart(_subtype=text_subtype) - payload_part.attach(text_part) - else: - # otherwise a single multipart/mixed will do the whole job - payload_part = text_part = MIMEMultipart(_subtype=text_subtype) - - if subject: - payload_part['Subject'] = subject - from_email = core_obj.email_id - if '<' in from_email: - # We have a structured email address, keep it untouched - payload_part['From'] = Header(core_obj.email_id, 'utf-8').encode() - else: - # Plain email address, construct a structured one based on the name: - sender_name = Header(core_obj.name, 'utf-8').encode() - payload_part['From'] = sender_name + " <" + core_obj.email_id + ">" - payload_part['Organization'] = tools.ustr(core_obj.user.company_id.name) - payload_part['Date'] = formatdate() - addresses_l = extract_emails_from_dict(addresses) - if addresses_l['To']: - payload_part['To'] = u','.join(addresses_l['To']) - if addresses_l['CC']: - payload_part['CC'] = u','.join(addresses_l['CC']) - if addresses_l['Reply-To']: - payload_part['Reply-To'] = addresses_l['Reply-To'][0] - if message_id: - payload_part['Message-ID'] = message_id - if body.get('text', False): - temp_body_text = body.get('text', '') - l = len(temp_body_text.replace(' ', '').replace('\r', '').replace('\n', '')) - if l == 0: - body['text'] = u'No Mail Message' - # Attach parts into message container. - # According to RFC 2046, the last part of a multipart message, in this case - # the HTML message, is best and preferred. - if core_obj.send_pref in ('text', 'mixed', 'alternative'): - body_text = body.get('text', u'<Empty Message>') - body_text = tools.ustr(body_text) - text_part.attach(MIMEText(body_text.encode("utf-8"), _charset='UTF-8')) - if core_obj.send_pref in ('html', 'mixed', 'alternative'): - html_body = body.get('html', u'') - if len(html_body) == 0 or html_body == u'': - html_body = body.get('text', u'<p><Empty Message></p>').replace('\n', '<br/>').replace('\r', '<br/>') - html_body = tools.ustr(html_body) - text_part.attach(MIMEText(html_body.encode("utf-8"), _subtype='html', _charset='UTF-8')) - - #Now add attachments if any, wrapping into a container multipart/mixed if needed - if payload: - for file in payload: - part = MIMEBase('application', "octet-stream") - part.set_payload(base64.decodestring(payload[file])) - part.add_header('Content-Disposition', 'attachment; filename="%s"' % file) - Encoders.encode_base64(part) - payload_part.attach(part) - except Exception, error: - logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:MIME Error\nDescription: %s") % (id, tools.ustr(error))) - return {'error_msg': _("Server Send Error\nDescription: %s")%error} - try: - serv.sendmail(payload_part['From'], addresses_l['all-recipients'], payload_part.as_string()) - except Exception, error: - logging.getLogger('email_template').error(_("Mail from Account %s failed. Probable Reason: Server Send Error\n Description: %s"), id, tools.ustr(error), exc_info=True) - return {'error_msg': _("Server Send Error\nDescription: %s") % tools.ustr(error)} - #The mail sending is complete - serv.close() - logger.notifyChannel(_("Email Template"), netsvc.LOG_INFO, _("Mail from Account %s successfully Sent.") % (id)) - return True - else: - logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id) - return {'nodestroy':True,'error_msg': _("Mail from Account %s failed. Probable Reason:Account not approved")% id} - - def extracttime(self, time_as_string): - """ - TODO: DOC THis - """ - logger = netsvc.Logger() - #The standard email dates are of format similar to: - #Thu, 8 Oct 2009 09:35:42 +0200 - date_as_date = False - convertor = {'+':1, '-':-1} - try: - time_as_string = time_as_string.replace(',', '') - date_list = time_as_string.split(' ') - date_temp_str = ' '.join(date_list[1:5]) - if len(date_list) >= 6: - sign = convertor.get(date_list[5][0], False) - else: - sign = False - try: - dt = datetime.datetime.strptime( - date_temp_str, - "%d %b %Y %H:%M:%S") - except: - try: - dt = datetime.datetime.strptime( - date_temp_str, - "%d %b %Y %H:%M") - except: - return False - if sign: - try: - offset = datetime.timedelta( - hours=sign * int( - date_list[5][1:3] - ), - minutes=sign * int( - date_list[5][3:5] - ) - ) - except Exception, e2: - """Looks like UT or GMT, just forget decoding""" - return False - else: - offset = datetime.timedelta(hours=0) - dt = dt + offset - date_as_date = dt.strftime('%Y-%m-%d %H:%M:%S') - except Exception, e: - logger.notifyChannel( - _("Email Template"), - netsvc.LOG_WARNING, - _( - "Datetime Extraction failed.Date:%s \ - \tError:%s") % ( - time_as_string, - tools.ustr(e)) - ) - return date_as_date - - def send_receive(self, cr, uid, ids, context=None): - for id in ids: - ctx = context.copy() - ctx['filters'] = [('account_id', '=', id)] - self.pool.get('email_template.mailbox').send_all_mail(cr, uid, [], context=ctx) - return True - - def decode_header_text(self, text): - """ Decode internationalized headers RFC2822. - To, CC, BCC, Subject fields can contain - text slices with different encodes, like: - =?iso-8859-1?Q?Enric_Mart=ED?= <enricmarti@company.com>, - =?Windows-1252?Q?David_G=F3mez?= <david@company.com> - Sometimes they include extra " character at the beginning/ - end of the contact name, like: - "=?iso-8859-1?Q?Enric_Mart=ED?=" <enricmarti@company.com> - and decode_header() does not work well, so we use regular - expressions (?= ? ? ?=) to split the text slices - """ - if not text: - return text - p = re.compile("(=\?.*?\?.\?.*?\?=)") - text2 = '' - try: - for t2 in p.split(text): - text2 += ''.join( - [s.decode( - t or 'ascii' - ) for (s, t) in decode_header(t2)] - ).encode('utf-8') - except: - return text - return text2 - -email_template_account() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/email_template_account_view.xml b/addons/email_template/email_template_account_view.xml deleted file mode 100644 index 09f9062d1877c8b072e85cad974d59110314cdac..0000000000000000000000000000000000000000 --- a/addons/email_template/email_template_account_view.xml +++ /dev/null @@ -1,124 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - - <menuitem name="Marketing" icon="terp-crm" id="base.marketing_menu" sequence="17" - groups="marketing.group_marketing_user,marketing.group_marketing_manager"/> - <menuitem name="Emails" id="base.menu_emails" parent="base.marketing_menu" sequence="5"/> - <menuitem name="Email Template" id="menu_email_template" parent="base.menu_emails"/> - - <record model="ir.ui.view" id="email_template_account_form"> - <field name="name">email_template.account.form</field> - <field name="model">email_template.account</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Email Account Configuration"> - <group colspan="2"> - <field name="name" select="1" /> - </group> - <notebook colspan="4"> - <page string="Outgoing"> - <separator string="Server Information" colspan="4" /> - <group colspan="4" col="4"> - <field name="smtpserver" select="1"/> - <button name="check_outgoing_connection" type="object" string="Test Outgoing Connection" icon="gtk-network" colspan="2"/> - <field name="smtpport" select="2" /> - <field name="smtpssl" select="2" /> - <field name="smtptls" select="2" /> - <field name="auto_delete" /> - </group> - <separator string="User Information" colspan="4" /> - <group col="2" colspan="2"> - <field name="email_id" select="1" colspan="2" /> - <field name="smtppass" password="True" colspan="2" /> - <field name="company" select="2" colspan="2" /> - </group> - <group col="2" colspan="2"> - <field name="smtpuname" select="1" colspan="2" /> - <field name="user" select="2" colspan="2" /> - <field name="send_pref" colspan="2" /> - </group> - </page> - </notebook> - <group colspan="4" col="10"> - <field name="state" select="1"/> - <button string="Approve Account" name="button_approval" states="draft" type="workflow" icon="terp-camera_test"/> - <button string="Suspend Account" name="button_suspended" states="approved" type="workflow" icon="gtk-cancel"/> - <button string="Request Re-activation" name="get_reapprove" states="suspended" type="workflow" icon="gtk-convert"/> - <button string="Send/Receive" name="send_receive" states="approved" type="object" icon="terp-check"/> - </group> - </form> - </field> - </record> - - <record model="ir.ui.view" id="email_template_account_tree"> - <field name="name">email_template.account.tree</field> - <field name="model">email_template.account</field> - <field name="type">tree</field> - <field name="arch" type="xml"> - <tree colors="blue:state == 'draft';black:state in ('suspended','approved')" string="Email Accounts"> - <field name="name" /> - <field name="email_id" /> - <field name="smtpuname" /> - <field name="user" /> - <field name="smtpserver" /> - <field name="smtpport" /> - <field name="auto_delete" /> - <field name="state" /> - </tree> - </field> - </record> - - <record id="view_email_template_account_search" model="ir.ui.view"> - <field name="name">email_template.account.search</field> - <field name="model">email_template.account</field> - <field name="type">search</field> - <field name="arch" type="xml"> - <search string="Email Accounts"> - <filter icon="terp-document-new" string="Draft" name="draft" domain="[('state','=','draft')]"/> - <filter icon="terp-camera_test" string="Approved" domain="[('state','=','approved')]"/> - <filter icon="terp-emblem-important" string="Suspended" domain="[('state','=','suspended')]"/> - <separator orientation="vertical"/> - <filter icon="terp-go-home" string="Company Accounts" domain="[('company','=','yes')]"/> - <separator orientation="vertical"/> - <field name="user" select="1"> - <filter icon="terp-personal" help="My Accounts" name="my" domain="[('user','=',uid)]"/> - </field> - <field name="name" select="1"/> - <field name="email_id" select="1"/> - </search> - </field> - </record> - - <record model="ir.actions.act_window" id="action_email_template_account_tree_all"> - <field name="name">Accounts</field> - <field name="res_model">email_template.account</field> - <field name="view_type">form</field> - <field name="view_mode">form,tree</field> - <field name="view_id" ref="email_template_account_tree" /> - <field name="context">{'search_default_draft': 1, 'search_default_my': 1}</field> - <field name="search_view_id" ref="view_email_template_account_search"/> - </record> - - <menuitem name="Configuration" parent="base.marketing_menu" - id="base.menu_marketing_config_root" sequence="20" groups="base.group_system"/> - - <menuitem name="Email Template" id="menu_email_template_configuration" parent="base.menu_marketing_config_root" /> - - <menuitem name="Email Accounts" id="menu_email_template_account_all" parent="menu_email_template_configuration" action="action_email_template_account_tree_all"/> - - <menuitem name="Configuration" parent="base.menu_tools" - id="base.menu_lunch_survey_root" sequence="20" /> - - <menuitem name="Email Template" id="menu_email_template_config_tools" - parent="base.menu_lunch_survey_root" /> - - <menuitem name="Email Accounts" id="menu_email_account_all_tools" - parent="menu_email_template_config_tools" action="action_email_template_account_tree_all" /> - - </data> -</openerp> - - - - diff --git a/addons/email_template/email_template_mailbox.py b/addons/email_template/email_template_mailbox.py deleted file mode 100644 index 4486cb5451e8e42fa78dd31ac3ac03295a8709c5..0000000000000000000000000000000000000000 --- a/addons/email_template/email_template_mailbox.py +++ /dev/null @@ -1,214 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2009 Sharoon Thomas -# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -############################################################################## - -from osv import osv, fields -import time -import netsvc -from tools.translate import _ -import tools - -LOGGER = netsvc.Logger() - -class email_template_mailbox(osv.osv): - _name = "email_template.mailbox" - _description = 'Email Mailbox' - _rec_name = "subject" - _order = "date_mail desc" - - def run_mail_scheduler(self, cursor, user, context=None): - """ - This method is called by OpenERP Scheduler - to periodically send emails - """ - try: - self.send_all_mail(cursor, user, context=context) - except Exception, e: - LOGGER.notifyChannel( - "Email Template", - netsvc.LOG_ERROR, - _("Error sending mail: %s") % e) - - def send_all_mail(self, cr, uid, ids=None, context=None): - if ids is None: - ids = [] - if context is None: - context = {} - filters = [('folder', '=', 'outbox'), ('state', '!=', 'sending')] - if 'filters' in context.keys(): - for each_filter in context['filters']: - filters.append(each_filter) - ids = self.search(cr, uid, filters, context=context) - self.write(cr, uid, ids, {'state':'sending'}, context) - self.send_this_mail(cr, uid, ids, context) - return True - - def send_this_mail(self, cr, uid, ids=None, context=None): - #previous method to send email (link with email account can be found at the revision 4172 and below - result = True - attachment_pool = self.pool.get('ir.attachment') - for id in (ids or []): - try: - account_obj = self.pool.get('email_template.account') - values = self.read(cr, uid, id, [], context) - payload = {} - if values['attachments_ids']: - for attid in values['attachments_ids']: - attachment = attachment_pool.browse(cr, uid, attid, context)#,['datas_fname','datas']) - payload[attachment.datas_fname] = attachment.datas - result = account_obj.send_mail(cr, uid, - [values['account_id'][0]], - {'To':values.get('email_to') or u'', - 'CC':values.get('email_cc') or u'', - 'BCC':values.get('email_bcc') or u'', - 'Reply-To':values.get('reply_to') or u''}, - values['subject'] or u'', - {'text':values.get('body_text') or u'', 'html':values.get('body_html') or u''}, - payload=payload, - message_id=values['message_id'], - context=context) - if result == True: - account = account_obj.browse(cr, uid, values['account_id'][0], context=context) - if account.auto_delete: - self.write(cr, uid, id, {'folder': 'trash'}, context=context) - self.unlink(cr, uid, [id], context=context) - # Remove attachments for this mail - attachment_pool.unlink(cr, uid, values['attachments_ids'], context=context) - return result - else: - self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context) - self.historise(cr, uid, [id], "Email sent successfully", context) - else: - error = result['error_msg'] - self.historise(cr, uid, [id], error, context) - - except Exception, error: - logger = netsvc.Logger() - logger.notifyChannel("email-template", netsvc.LOG_ERROR, _("Sending of Mail %s failed. Probable Reason:Could not login to server\nError: %s") % (id, error)) - self.historise(cr, uid, [id], error, context) - self.write(cr, uid, id, {'state':'na'}, context) - return result - - def historise(self, cr, uid, ids, message='', context=None): - for id in ids: - history = self.read(cr, uid, id, ['history'], context).get('history', '') - self.write(cr, uid, id, {'history': (history or '' )+ "\n" + time.strftime("%Y-%m-%d %H:%M:%S") + ": " + tools.ustr(message)}, context) - - _columns = { - 'email_from':fields.char( - 'From', - size=64), - 'email_to':fields.char( - 'Recipient (To)', - size=250,), - 'email_cc':fields.char( - 'CC', - size=250), - 'email_bcc':fields.char( - 'BCC', - size=250), - 'reply_to':fields.char( - 'Reply-To', - size=250), - 'message_id':fields.char( - 'Message-ID', - size=250), - 'subject':fields.char( - 'Subject', - size=200,), - 'body_text':fields.text( - 'Standard Body (Text)'), - 'body_html':fields.text( - 'Body (Rich Text Clients Only)'), - 'attachments_ids':fields.many2many( - 'ir.attachment', - 'mail_attachments_rel', - 'mail_id', - 'att_id', - 'Attachments'), - 'account_id' :fields.many2one( - 'email_template.account', - 'User account', - required=True), - 'user':fields.related( - 'account_id', - 'user', - type="many2one", - relation="res.users", - string="User"), - 'server_ref':fields.integer( - 'Server Reference of mail', - help="Applicable for inward items only"), - 'mail_type':fields.selection([ - ('multipart/mixed', - 'Has Attachments'), - ('multipart/alternative', - 'Plain Text & HTML with no attachments'), - ('multipart/related', - 'Intermixed content'), - ('text/plain', - 'Plain Text'), - ('text/html', - 'HTML Body'), - ], 'Mail Contents'), - #I like GMAIL which allows putting same mail in many folders - #Lets plan it for 0.9 - 'folder':fields.selection([ - ('drafts', 'Drafts'), - ('outbox', 'Outbox'), - ('trash', 'Trash'), - ('sent', 'Sent Items'), - ], 'Folder', required=True), - 'state':fields.selection([ - ('na', 'Not Applicable'), - ('sending', 'Sending'), - ], 'Status', required=True), - 'date_mail':fields.datetime('Rec/Sent Date', help="Date on which Email Sent or Received"), - 'history':fields.text( - 'History', - readonly=True, - store=True) - } - - _defaults = { - 'state': lambda * a: 'na', - 'folder': lambda * a: 'outbox', - } - - def unlink(self, cr, uid, ids, context=None): - """ - It just changes the folder of the item to "Trash", if it is no in Trash folder yet, - or completely deletes it if it is already in Trash. - """ - to_update = [] - to_remove = [] - for mail in self.browse(cr, uid, ids, context=context): - if mail.folder == 'trash': - to_remove.append(mail.id) - else: - to_update.append(mail.id) - # Changes the folder to trash - self.write(cr, uid, to_update, {'folder': 'trash'}, context=context) - return super(email_template_mailbox, self).unlink(cr, uid, to_remove, context=context) - -email_template_mailbox() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/email_template_mailbox_view.xml b/addons/email_template/email_template_mailbox_view.xml deleted file mode 100644 index 0d1b3b0a2bf77209b46e0a96db1d6a38f049dc57..0000000000000000000000000000000000000000 --- a/addons/email_template/email_template_mailbox_view.xml +++ /dev/null @@ -1,134 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<openerp> - <data> - <!-- Email Template--> - <record model="ir.ui.view" id="email_template_mailbox_form"> - <field name="name">email_template.mailbox.form</field> - <field name="model">email_template.mailbox</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Emails"> - <group col="4" colspan="4" name="headers"> - <field name="email_from" select="1"/> - <field name="email_to" required="1" select="1" /> - <field name="reply_to" select="2"/> - <field name="email_cc" select="1"/> - <field name="email_bcc" select="2"/> - <field name="date_mail" select="2"/> - <field name="subject" colspan="4" select="1"/> - </group> - <notebook colspan="4"> - <page string="Standard Body"> - <separator colspan="4" string="Standard Body" /> - <notebook colspan="4"> - <page string="Standard Body (Text)"> - <field name="body_text" nolabel="1" colspan="4" select="1"/> - </page> - <page string="Body (HTML-Web Client Only)"> - <field name="body_html" nolabel="1" colspan="4" /> - </page> - </notebook> - <separator colspan="4" string="" /> - <group col="4" colspan="4"> - <field name="state" readonly="1" string="State"/> - <button name="send_this_mail" type="object" string="Send Mail" icon="terp-mail-message-new"/> - </group> - </page> - - <page string="Attachments"> - <separator colspan="4" string="Attachments" /> - <field name="attachments_ids" colspan="4" nolabel="1" /> - </page> - <page string="Advanced"> - <field name="account_id" colspan="2" /> - <field name="server_ref" colspan="2" /> - <field name="mail_type" colspan="2" /> - <field name="folder" colspan="2" select="2"/> - <field name="message_id" select="2"/> - <separator string="History" colspan="4" /> - <field name="history" nolabel="1" colspan="4"/> - </page> - </notebook> - </form> - </field> - </record> - - <!--============================================= TREE VIEWS =============================================--> - <record id="view_email_template_mailbox_tree" model="ir.ui.view"> - <field name="name">email_template.mailbox.tree</field> - <field name="model">email_template.mailbox</field> - <field name="type">tree</field> - <field name="arch" type="xml"> - <tree string="Emails" colors="blue:folder=='drafts';grey:folder=='trash'"> - <field name="subject" select="1" /> - <field name="user" /> - <field name="email_from" select="1" /> - <field name="email_to"/> - <field name="date_mail" select="2" /> - <field name="attachments_ids" select="2" /> - <field name="folder" invisible="1"/> - </tree> - </field> - </record> - - <record id="view_email_template_mailbox_search" model="ir.ui.view"> - <field name="name">email_template.mailbox.search</field> - <field name="model">email_template.mailbox</field> - <field name="type">search</field> - <field name="arch" type="xml"> - <search string="Emails"> - <filter icon="terp-document-new" string="Drafts" name="draft" domain="[('folder','=','drafts')]"/> - <filter icon="terp-mail-" string="Outbox" name="outbox" domain="[('folder','=','outbox')]"/> - <filter icon="terp-camera_test" string="Sent" domain="[('folder','=','sent')]"/> - <filter icon="terp-mail_delete" string="Trash" domain="[('folder','=','trash')]"/> - <separator orientation="vertical"/> - <filter icon="terp-personal+" string="Personal Emails" name="personal" domain="[('account_id.company','=','no')]"/> - <filter icon="terp-go-home" string="Company Emails" name="company" domain="[('account_id.company','=','yes')]"/> - <separator orientation="vertical"/> - <field name="subject"/> - <field name="email_from"/> - <field name="user"> - <filter icon="terp-personal" - string="My Emails" help="My Emails" name="myemails" - domain="[('account_id.user','=', uid)]" /> - </field> - <newline/> - <field name="email_to"/> - <field name="date_mail"/> - </search> - </field> - </record> - - <record model="ir.actions.act_window" id="action_email_template_mailbox"> - <field name="name">Emails</field> - <field name="res_model">email_template.mailbox</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="view_id" ref="view_email_template_mailbox_tree" /> - <field name="context">{'search_default_outbox': 1}</field> - <field name="help">An email template is an email document that will be sent as part of a marketing campaign. You can personalize it according to specific customer profile fields, so that a partner name or other partner related information may be inserted automatically.</field> - <field name="search_view_id" ref="view_email_template_mailbox_search"/> - </record> - - <!--======================================== MENUS ========================================--> - - <menuitem name="Emails" - id="menu_email_template_personal_mails" - parent="menu_email_template" - action="action_email_template_mailbox" /> - - <!-- Mailbox menu in Tools --> - <menuitem name="Email Template" id="menu_email_template_tools" - parent="base.menu_tools" /> - - <menuitem name="Emails" - id="menu_email_template_mails_tools" - parent="menu_email_template_tools" - action="action_email_template_mailbox" /> - - </data> -</openerp> - - - - diff --git a/addons/email_template/email_template_view.xml b/addons/email_template/email_template_view.xml index 7d2a5f7ad5ca1dcfec20423e25d16ac182fc591e..b4f76a03c6cfd4bd0afc1889deabc35581eaa7f6 100644 --- a/addons/email_template/email_template_view.xml +++ b/addons/email_template/email_template_view.xml @@ -1,141 +1,99 @@ <?xml version="1.0" encoding="UTF-8"?> <openerp> <data> - - <!-- Email Template Preview --> - <record model="ir.ui.view" id="email_template_preview_form"> - <field name="name">email_template.preview.form</field> - <field name="model">email_template.preview</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Email Preview"> - <group col="4" colspan="4"> - <field name="rel_model" /> - <field name="rel_model_ref" on_change="on_change_ref(rel_model_ref, context)"/> - </group> - <group col="8" colspan="4"> - <field name="to" /> - <field name="cc" /> - <field name="bcc" /> - <field name="reply_to" /> - <field name="message_id" attrs="{'invisible':[('message_id','=',False)]}" groups="base.group_extended"/> - <field name="subject" colspan="8"/> - </group> - <group col="4" colspan="4"> - <separator string= "Body(Text)" colspan="2"/> - <separator string= "Body(Html)" colspan="2"/> - <newline/> - <field name="body_text" nolabel="1" colspan="2"/> - <field name="body_html" nolabel="1" colspan="2"/> - </group> - <field name="report" colspan="2"/> - <button icon="gtk-ok" special="cancel" string="OK" colspan="1"/> - </form> - </field> - </record> - - <record id="wizard_email_template_preview" model="ir.actions.act_window"> - <field name="name">Template Preview</field> - <field name="res_model">email_template.preview</field> - <field name="src_model">email_template.preview</field> - <field name="type">ir.actions.act_window</field> - <field name="view_type">form</field> - <field name="view_mode">form</field> - <field name="auto_refresh" eval="1" /> - <field name="target">new</field> - <field name="context">{'template_id':active_id}</field> - </record> - <!--EMail client Form view --> <record model="ir.ui.view" id="email_template_form"> <field name="name">email.template.form</field> <field name="model">email.template</field> <field name="type">form</field> <field name="arch" type="xml"> - <form string="Email Templates"> - <field name="name" /> - <field name="object_name" required="1" - on_change="change_model(object_name)" /> - <field name="model_int_name" invisible="1" /> + <form string="Templates"> + <field name="name" required="1"/> + <field name="model_id" required="1" on_change="onchange_model_id(model_id)"/> + <field name="model" invisible="1"/> <notebook colspan="4"> - <page string="Mail Details"> + <page string="Email Details"> <group col="2" colspan="2"> <separator string="Addresses" colspan="2"/> - <field name="from_account" required="1"/> - <field name="def_to" required="1"/> - <field name="def_cc"/> - <field name="def_bcc"/> + <field name="email_from" required="1"/> + <field name="email_to" required="1"/> + <field name="email_cc"/> + <field name="email_bcc"/> <field name="reply_to"/> </group> <group col="2" colspan="2"> <separator string="Options" colspan="2"/> <field name="lang" colspan="4" /> - <field name="use_sign" colspan="4" /> - <field name="track_campaign_item" colspan="4"/> + <field name="user_signature" colspan="4" /> </group> <group col="2" colspan="2"> - <separator colspan="2" string="Email Content " /> - <field name="def_subject" colspan="4" required="1" /> + <separator colspan="2" string="Email Content"/> + <field name="subject" colspan="4" required="1"/> <notebook> <page string="Body (Text)"> - <field name="def_body_text" colspan="4" nolabel="1" /> + <field name="body_text" colspan="4" width="250" height="250" nolabel="1"/> </page> - <page string="Body (Raw HTML)"> - <field name="def_body_html" colspan="4" nolabel="1" /> - <label string="Note: This is Raw HTML." colspan="4" /> + <page string="Body (Rich/HTML)"> + <field name="body_html" colspan="4" width="250" height="250" nolabel="1"/> + <label string="Note: This is Raw HTML." colspan="4"/> </page> </notebook> </group> <group col="4" colspan="2"> - <separator colspan="4" string="Expression Builder" /> - <field name="template_language" - on_change="onchange_null_value(model_object_field,sub_model_object_field,null_value,template_language,context)" /> <notebook> - <page string="Insert Simple Field"> - + <page string="Dynamic Values Builder"> <field name="model_object_field" - domain="[('model_id','=',object_name),('ttype','!=','one2many'),('ttype','!=','many2many')]" - on_change="onchange_model_object_field(model_object_field, template_language,context)" - colspan="4" /> - <field name="sub_object" readonly="1" colspan="4" /> + domain="[('model_id','=',model_id),('ttype','!=','one2many'),('ttype','!=','many2many')]" + on_change="onchange_sub_model_object_value_field(model_object_field)" + colspan="4"/> + <field name="sub_object" readonly="1" colspan="4"/> <field name="sub_model_object_field" domain="[('model_id','=',sub_object),('ttype','!=','one2many'),('ttype','!=','many2many')]" colspan="4" attrs="{'readonly':[('sub_object','=',False)],'required':[('sub_object','!=',False)]}" - on_change="onchange_sub_model_object_field(model_object_field,sub_model_object_field,template_language,context)" /> + on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field)"/> <field name="null_value" colspan="4" - on_change="onchange_null_value(model_object_field,sub_model_object_field,null_value,template_language,context)" /> - <field name="copyvalue" colspan="4" /> + on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field,null_value)" /> + <field name="copyvalue" colspan="4"/> </page> </notebook> <button name="%(wizard_email_template_preview)d" string="Preview Template" - type="action" colspan="4" target="new" icon="gtk-zoom-fit"/> + type="action" colspan="4" target="new" icon="gtk-zoom-fit" context="{'template_id':active_id}"/> </group> </page> <page string="Advanced"> <group colspan="2" col="2"> <group colspan="2" col="2"> - <separator string="Actions" colspan="2"/> - <button name="create_action" string="Create Action" type="object" icon="gtk-execute" colspan="2" attrs="{'invisible':[('ref_ir_act_window','!=',False), ('ref_ir_value','!=',False)]}"/> - <field name="ref_ir_act_window"/> - <field name="ref_ir_value"/> - <button name="delete_action" string="Delete Action" type="object" icon="gtk-delete" colspan="2" attrs="{'invisible':[('ref_ir_act_window','=',False), ('ref_ir_value','=',False)]}"/> + <separator string="Sidebar button" colspan="2"/> + <button name="create_action" string="Add sidebar button" type="object" icon="gtk-execute" + colspan="2" attrs="{'invisible':[('ref_ir_act_window','!=',False)]}" + help="Display a button in the sidebar of related documents to open a composition wizard with this template" + /> + <field name="ref_ir_act_window" attrs="{'invisible':[('ref_ir_act_window','=',False)]}"/> + <field name="ref_ir_value" attrs="{'invisible':[('ref_ir_act_window','=',False)]}"/> + <button name="unlink_action" string="Remove sidebar button" type="object" icon="gtk-delete" + colspan="2" attrs="{'invisible':[('ref_ir_act_window','=',False)]}" + help="Remove the sidebar button currently displayed on related documents" + /> </group> - <group colspan="2" col="2"> + <group colspan="2" col="2" groups="base.group_extended"> <separator string="Advanced Options" colspan="2"/> + <field name="mail_server_id"/> + <field name="track_campaign_item" colspan="4"/> <field name="message_id"/> + <field name="auto_delete"/> </group> </group> <group colspan="2" col="2"> <separator string="Attachments" colspan="2"/> <notebook> - <page string="Existing files"> - <field name="attachment_ids" colspan="4" nolabel="1" height="350"/> - </page> - <page string="Report"> + <page string="Attach Report"> <field name="report_template" colspan="4" - domain="[('model','=',model_int_name)]" /> - <field name="file_name" colspan="4" /> + domain="[('model','=',model)]"/> + <field name="report_name" colspan="4" /> + </page> + <page string="Attach existing files"> + <field name="attachment_ids" colspan="4" nolabel="1" height="350"/> </page> </notebook> </group> @@ -150,16 +108,13 @@ <field name="model">email.template</field> <field name="type">tree</field> <field name="arch" type="xml"> - <tree string="Email Templates"> + <tree string="Templates"> + <field name="model_id"/> <field name="name"/> - <field name="from_account"/> - <field name="object_name"/> - <field name="def_to"/> - <field name="def_cc"/> - <field name="def_bcc"/> - <field name="def_subject"/> - <field name="use_sign"/> - <field name="file_name"/> + <field name="subject"/> + <field name="email_from"/> + <field name="email_to"/> + <field name="report_name"/> <button name="%(wizard_email_template_preview)d" string="Preview Template" type="action" target="new" icon="gtk-zoom-fit"/> </tree> @@ -173,26 +128,26 @@ <field name="arch" type="xml"> <search string="Templates"> <group col="13" colspan="4"> - <field name="name" select="1"/> - <field name="object_name" select="1"/> - <field name="def_to" select="1"/> + <field name="name"/> + <field name="model_id"/> + <field name="email_to"/> <separator orientation="vertical"/> - <field name="lang" select="1"/> - <field name="def_subject" select="1"/> - <field name="file_name" select="1"/> + <field name="lang"/> + <field name="subject"/> + <field name="report_name"/> </group> <newline/> <group expand="0" string="Group by..." colspan="4" col="10"> - <filter string="Account" domain="[]" context="{'group_by':'from_account'}" icon="terp-folder-orange"/> + <filter string="SMTP Server" domain="[]" context="{'group_by':'smtp_server_id'}" icon="terp-folder-orange"/> <separator orientation="vertical"/> - <filter string="Resource" domain="[]" context="{'group_by':'object_name'}" icon="terp-accessories-archiver"/> + <filter string="Model" domain="[]" context="{'group_by':'model_id'}" icon="terp-accessories-archiver"/> </group> </search> </field> </record> <record model="ir.actions.act_window" id="action_email_template_tree_all"> - <field name="name">Email Templates</field> + <field name="name">Templates</field> <field name="res_model">email.template</field> <field name="view_type">form</field> <field name="view_mode">form,tree</field> @@ -200,16 +155,9 @@ <field name="search_view_id" ref="view_email_template_search"/> </record> - <menuitem name="Email Templates" id="menu_email_template_all" - parent="menu_email_template" action="action_email_template_tree_all" /> + <menuitem name="Templates" id="menu_email_template_all_tools" + parent="mail.menu_email_message_tools" action="action_email_template_tree_all" /> + - <!-- Email Template menu in Tools --> - <menuitem name="Email Templates" id="menu_email_template_all_tools" - parent="menu_email_template_config_tools" action="action_email_template_tree_all" /> </data> </openerp> - - - - - diff --git a/addons/email_template/email_template_workflow.xml b/addons/email_template/email_template_workflow.xml deleted file mode 100644 index 84a58815b5e9f629b306c5dd16cfe3ec1b70f7a5..0000000000000000000000000000000000000000 --- a/addons/email_template/email_template_workflow.xml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - - <record id="wkf_email_template_setting" model="workflow"> - <field name="name">Email Template Workflow</field> - <field name="osv">email_template.account</field> - <field name="on_create">True</field> - </record> - - <!--Activity --> - - <record id="act_draft" model="workflow.activity"> - <field name="wkf_id" ref="wkf_email_template_setting"/> - <field name="flow_start">True</field> - <field name="name">draft</field> - <field name="kind">function</field> - <field name="action">write({'state':'draft'})</field> - </record> - - <record id="act_approved" model="workflow.activity"> - <field name="name">approval</field> - <field name="wkf_id" ref="wkf_email_template_setting"/> - <field name="kind">function</field> - <field name="action">do_approval()</field> - </record> - - <record id="act_suspended" model="workflow.activity"> - <field name="name">suspended</field> - <field name="wkf_id" ref="wkf_email_template_setting"/> - <field name="kind">function</field> - <field name="action">write({'state':'suspended'})</field> - </record> - <record id="act_dummy" model="workflow.activity"> - <field name="name">dummy</field> - <field name="wkf_id" ref="wkf_email_template_setting"/> - <field name="flow_stop">True</field> - </record> - - <!-- Transition --> - - <record id="trans_awaiting_approved" model="workflow.transition"> - <field name="act_from" ref="act_draft"/> - <field name="act_to" ref="act_approved"/> - <field name="signal">button_approval</field> - </record> - - <record id="trans_approved_suspended" model="workflow.transition"> - <field name="act_from" ref="act_approved"/> - <field name="act_to" ref="act_suspended"/> - <field name="signal">button_suspended</field> - </record> - <record id="trans_suspended_reapproved" model="workflow.transition"> - <field name="act_from" ref="act_suspended"/> - <field name="act_to" ref="act_draft"/> - <field name="signal">get_reapprove</field> - </record> - <record id="trans_suspended_dummy" model="workflow.transition"> - <field name="act_from" ref="act_suspended"/> - <field name="act_to" ref="act_dummy"/> - <field name="signal">get_never</field> - </record> - </data> -</openerp> diff --git a/addons/email_template/security/email_template_security.xml b/addons/email_template/security/email_template_security.xml deleted file mode 100644 index e3f3401217c374546bfa27f791494a3127f85c10..0000000000000000000000000000000000000000 --- a/addons/email_template/security/email_template_security.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data noupdate="0"> - - </data> -</openerp> diff --git a/addons/email_template/security/ir.model.access.csv b/addons/email_template/security/ir.model.access.csv index 17859d6bb9d8d4a847c81a22d5cfd270ee028f07..f5f61240a9658cdde84f43e73ab608c133f70ba5 100644 --- a/addons/email_template/security/ir.model.access.csv +++ b/addons/email_template/security/ir.model.access.csv @@ -1,10 +1,4 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_email_template_account","email_template.account","model_email_template_account","marketing.group_marketing_user",1,0,0,0 -"access_email_template","email.template","model_email_template","marketing.group_marketing_user",1,0,0,0 -"access_email_template_mailbox","email_template.mailbox","model_email_template_mailbox","marketing.group_marketing_user",1,1,1,1 -"access_email_template_account_system","email_template.account system","model_email_template_account","base.group_system",1,1,1,1 +"access_email_template","email.template","model_email_template",,1,0,0,0 "access_email_template_system","email.template system","model_email_template","base.group_system",1,1,1,1 -"access_email_template_mailbox_system","email_template.mailbox system","model_email_template_mailbox","base.group_system",1,0,0,0 -"access_email_template_account_manager","email_template.account","model_email_template_account","marketing.group_marketing_manager",1,1,1,1 -"access_email_template_manager","email.template","model_email_template","marketing.group_marketing_manager",1,1,1,1 -"access_email_template_mailbox_manager","email_template.mailbox","model_email_template_mailbox","marketing.group_marketing_manager",1,1,1,1 +"access_email_template_manager","email.template","model_email_template",,1,1,1,1 diff --git a/addons/email_template/wizard/__init__.py b/addons/email_template/wizard/__init__.py index 1641152490e3dda488a750552e64907b8d04dc26..b9ac29ec4c2177b875e2f6caa8fd2f417ee7f551 100644 --- a/addons/email_template/wizard/__init__.py +++ b/addons/email_template/wizard/__init__.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2009 Sharoon Thomas -# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>) +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,5 +19,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/> # ############################################################################## +import email_template_preview +import mail_compose_message -import email_template_send_wizard diff --git a/addons/email_template/wizard/email_compose_message_view.xml b/addons/email_template/wizard/email_compose_message_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..cf233b20682270fd820cd27f940c00d7231c159e --- /dev/null +++ b/addons/email_template/wizard/email_compose_message_view.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + + <record model="ir.ui.view" id="email_compose_message_wizard_inherit_form"> + <field name="name">mail.compose.message.form</field> + <field name="model">mail.compose.message</field> + <field name="type">form</field> + <field name="inherit_id" ref="mail.email_compose_message_wizard_form"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//label[@name='placeholder']" position="before"> + <group attrs="{'invisible':[('use_template','=',False)]}" colspan="4" col="4"> + <field name="template_id" colspan="3" + on_change="on_change_template(use_template, template_id, email_from, email_to, context)"/> + <label string="" name="flexspace" colspan="1"/> + </group> + <group colspan="1" col="6"> + <field name="use_template" invisible="1"/> + <button icon="gtk-paste" type="object" name="template_toggle" + string="" help="Use a message template" colspan="1"/> + <button icon="gtk-save" type="object" name="save_as_template" + string="" help="Save as a new template" colspan="1"/> + </group> + </xpath> + </data> + </field> + </record> + </data> +</openerp> diff --git a/addons/email_template/wizard/email_template_preview.py b/addons/email_template/wizard/email_template_preview.py new file mode 100644 index 0000000000000000000000000000000000000000..650b714866d1aef4ae9977e667ad26ba91b7fcb7 --- /dev/null +++ b/addons/email_template/wizard/email_template_preview.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2009 Sharoon Thomas +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +from osv import osv, fields + +class email_template_preview(osv.osv_memory): + _inherit = "email.template" + _name = "email_template.preview" + _description = "Email Template Preview" + _rec_name = "subject" + + def _get_records(self, cr, uid, context=None): + """ + Return Records of particular Email Template's Model + """ + if context is None: + context = {} + + template_id = context.get('template_id', False) + if not template_id: + return [] + email_template = self.pool.get('email.template') + template = email_template.browse(cr, uid, int(template_id), context=context) + template_object = template.model_id + model = self.pool.get(template_object.model) + record_ids = model.search(cr, uid, [], 0, 10, 'id', context=context) + default_id = context.get('default_res_id') + + if default_id and default_id not in record_ids: + record_ids.insert(0, default_id) + + return model.name_get(cr, uid, record_ids, context) + + + def default_get(self, cr, uid, fields, context=None): + if context is None: + context = {} + result = super(email_template_preview, self).default_get(cr, uid, fields, context=context) + + email_template = self.pool.get('email.template') + template_id = context.get('template_id') + if 'res_id' in fields and not result.get('res_id'): + records = self._get_records(cr, uid, context=context) + result['res_id'] = records and records[0][0] or False # select first record as a Default + if template_id and 'model_id' in fields and not result.get('model_id'): + result['model_id'] = email_template.read(cr, uid, int(template_id), ['model_id'], context).get('model_id', False) + return result + + _columns = { + 'res_id':fields.selection(_get_records, 'Sample Document'), + } + + def on_change_res_id(self, cr, uid, ids, res_id, context=None): + if not res_id: + return {} + vals = {} + email_template = self.pool.get('email.template') + template_id = context and context.get('template_id') + template = email_template.get_email_template(cr, uid, template_id=template_id, record_id=res_id, context=context) + model = template.model + vals['email_to'] = self.render_template(cr, uid, template.email_to, model, res_id, context) + vals['email_cc'] = self.render_template(cr, uid, template.email_cc, model, res_id, context) + vals['email_bcc'] = self.render_template(cr, uid, template.email_bcc, model, res_id, context) + vals['reply_to'] = self.render_template(cr, uid, template.reply_to, model, res_id, context) + vals['subject'] = self.render_template(cr, uid, template.subject, model, res_id, context) + description = self.render_template(cr, uid, template.body_text, model, res_id, context) or '' + if template.user_signature: + signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature + description += '\n' + signature + vals['body_text'] = description + vals['report_name'] = self.render_template(cr, uid, template.report_name, model, res_id, context) + return {'value': vals} + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/wizard/email_template_preview_view.xml b/addons/email_template/wizard/email_template_preview_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..4aab57396ad44ea087baec6d7cfc98eabb8ab5f1 --- /dev/null +++ b/addons/email_template/wizard/email_template_preview_view.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <!-- Email Template Preview --> + <record model="ir.ui.view" id="email_template_preview_form"> + <field name="name">email_template.preview.form</field> + <field name="model">email_template.preview</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Email Preview"> + <field name="model_id" invisible="1"/> + <field name="res_id" on_change="on_change_res_id(res_id, context)"/> + <group col="2" colspan="4"> + <field name="email_to" readonly="1"/> + <field name="email_cc" readonly="1" attrs="{'invisible': [('email_cc','=',False)]}"/> + <field name="email_bcc" readonly="1" attrs="{'invisible': [('email_bcc','=',False)]}"/> + <field name="reply_to" readonly="1" attrs="{'invisible': [('reply_to','=',False)]}"/> + <field name="subject" readonly="1"/> + </group> + <group col="4" colspan="4"> + <notebook> + <page string="Body (Text)"> + <field name="body_text" nolabel="1" colspan="4" height="350" width="350" readonly="1"/> + </page> + <page string="Body (Rich/HTML)" attrs="{'invisible': [('body_html','=', False)]}"> + <field name="body_html" nolabel="1" colspan="4" height="350" width="350" readonly="1"/> + </page> + </notebook> + </group> + <field name="report_name" colspan="4" readonly="1"/> + <separator colspan="4"/> + <group col="4" colspan="4"> + <label string=""/> + <button icon="gtk-ok" special="cancel" string="Close" colspan="1"/> + </group> + </form> + </field> + </record> + + <record id="wizard_email_template_preview" model="ir.actions.act_window"> + <field name="name">Template Preview</field> + <field name="res_model">email_template.preview</field> + <field name="src_model">email_template.preview</field> + <field name="type">ir.actions.act_window</field> + <field name="view_type">form</field> + <field name="view_mode">form</field> + <field name="auto_refresh" eval="1" /> + <field name="target">new</field> + <field name="context">{'template_id':active_id}</field> + </record> + + </data> +</openerp> diff --git a/addons/email_template/wizard/email_template_send_wizard.py b/addons/email_template/wizard/email_template_send_wizard.py deleted file mode 100644 index 4f6ab7af381a5d6c1141f3397ad1f35eb174e58c..0000000000000000000000000000000000000000 --- a/addons/email_template/wizard/email_template_send_wizard.py +++ /dev/null @@ -1,279 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2009 Sharoon Thomas -# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -############################################################################## - -from osv import osv, fields -from mako.template import Template -from mako import exceptions -import netsvc -import base64 -from tools.translate import _ -import tools -from email_template.email_template import get_value - - -## FIXME: this wizard duplicates a lot of features of the email template preview, -## one of the 2 should inherit from the other! - -class email_template_send_wizard(osv.osv_memory): - _name = 'email_template.send.wizard' - _description = 'This is the wizard for sending mail' - _rec_name = "subject" - - def _get_accounts(self, cr, uid, context=None): - if context is None: - context = {} - - template = self._get_template(cr, uid, context) - if not template: - return [] - - logger = netsvc.Logger() - - if template.from_account: - return [(template.from_account.id, '%s (%s)' % (template.from_account.name, template.from_account.email_id))] - else: - account_id = self.pool.get('email_template.account').search(cr,uid,[('company','=','no'),('user','=',uid)], context=context) - if account_id: - account = self.pool.get('email_template.account').browse(cr,uid,account_id, context) - return [(r.id,r.name + " (" + r.email_id + ")") for r in account] - else: - logger.notifyChannel(_("email-template"), netsvc.LOG_ERROR, _("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal email account.")) - raise osv.except_osv(_("Missing mail account"),_("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal email account.")) - - def get_value(self, cursor, user, template, message, context=None, id=None): - """Gets the value of the message parsed with the content of object id (or the first 'src_rec_ids' if id is not given)""" - if not message: - return '' - if not id: - id = context['src_rec_ids'][0] - return get_value(cursor, user, id, message, template, context) - - def _get_template(self, cr, uid, context=None): - if context is None: - context = {} - if not 'template' in context and not 'template_id' in context: - return None - if 'template_id' in context.keys(): - template_ids = self.pool.get('email.template').search(cr, uid, [('id','=',context['template_id'])], context=context) - elif 'template' in context.keys(): - # Old versions of email_template used the name of the template. This caused - # problems when the user changed the name of the template, but we keep the code - # for compatibility with those versions. - template_ids = self.pool.get('email.template').search(cr, uid, [('name','=',context['template'])], context=context) - if not template_ids: - return None - - template = self.pool.get('email.template').browse(cr, uid, template_ids[0], context) - - lang = self.get_value(cr, uid, template, template.lang, context) - if lang: - # Use translated template if necessary - ctx = context.copy() - ctx['lang'] = lang - template = self.pool.get('email.template').browse(cr, uid, template.id, ctx) - return template - - def _get_template_value(self, cr, uid, field, context=None): - if context is None: - context = {} - template = self._get_template(cr, uid, context) - if not template: - return False - if len(context['src_rec_ids']) > 1: # Multiple Mail: Gets original template values for multiple email change - return getattr(template, field) - else: # Simple Mail: Gets computed template values - return self.get_value(cr, uid, template, getattr(template, field), context) - - _columns = { - 'state':fields.selection([ - ('single','Simple Mail Wizard Step 1'), - ('multi','Multiple Mail Wizard Step 1'), - ('done','Wizard Complete') - ],'Status',readonly=True), - 'ref_template':fields.many2one('email.template','Template',readonly=True), - 'rel_model':fields.many2one('ir.model','Model',readonly=True), - 'rel_model_ref':fields.integer('Referred Document',readonly=True), - 'from':fields.selection(_get_accounts,'From Account',select=True), - 'to':fields.char('To',size=250,required=True), - 'cc':fields.char('CC',size=250,), - 'bcc':fields.char('BCC',size=250,), - 'reply_to':fields.char('Reply-To', - size=250, - help="The address recipients should reply to," - " if different from the From address." - " Placeholders can be used here."), - 'message_id':fields.char('Message-ID', - size=250, - help="The Message-ID header value, if you need to" - "specify it, for example to automatically recognize the replies later." - " Placeholders can be used here."), - 'subject':fields.char('Subject',size=200), - 'body_text':fields.text('Body',), - 'body_html':fields.text('Body',), - 'report':fields.char('Report File Name',size=100,), - 'signature':fields.boolean('Attach my signature to mail'), - #'filename':fields.text('File Name'), - 'requested':fields.integer('No of requested Mails',readonly=True), - 'generated':fields.integer('No of generated Mails',readonly=True), - 'full_success':fields.boolean('Complete Success',readonly=True), - 'attachment_ids': fields.many2many('ir.attachment','send_wizard_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'), - } - - #FIXME: probably better by overriding default_get directly - _defaults = { - 'state': lambda self,cr,uid,ctx: len(ctx['src_rec_ids']) > 1 and 'multi' or 'single', - 'rel_model': lambda self,cr,uid,ctx: self.pool.get('ir.model').search(cr,uid,[('model','=',ctx['src_model'])],context=ctx)[0], - 'rel_model_ref': lambda self,cr,uid,ctx: ctx['active_id'], - 'to': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_to', ctx), - 'cc': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_cc', ctx), - 'bcc': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_bcc', ctx), - 'subject':lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_subject', ctx), - 'body_text':lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_body_text', ctx), - 'body_html':lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'def_body_html', ctx), - 'report': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'file_name', ctx), - 'signature': lambda self,cr,uid,ctx: self._get_template(cr, uid, ctx).use_sign, - 'ref_template':lambda self,cr,uid,ctx: self._get_template(cr, uid, ctx).id, - 'reply_to': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'reply_to', ctx), - 'reply_to': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'reply_to', ctx), - 'requested':lambda self,cr,uid,ctx: len(ctx['src_rec_ids']), - 'full_success': False, - 'attachment_ids': [], - } - - def fields_get(self, cr, uid, fields=None, context=None, write_access=True): - if context is None: - context = {} - result = super(email_template_send_wizard, self).fields_get(cr, uid, fields, context, write_access) - if 'attachment_ids' in result and 'src_model' in context: - result['attachment_ids']['domain'] = [('res_model','=',context['src_model']),('res_id','=',context['active_id'])] - return result - - def sav_to_drafts(self, cr, uid, ids, context=None): - if context is None: - context = {} - mailid = self.save_to_mailbox(cr, uid, ids, context=context) - if self.pool.get('email_template.mailbox').write(cr, uid, mailid, {'folder':'drafts'}, context): - return {'type':'ir.actions.act_window_close' } - - def send_mail(self, cr, uid, ids, context=None): - if context is None: - context = {} - mailid = self.save_to_mailbox(cr, uid, ids, context) - if self.pool.get('email_template.mailbox').write(cr, uid, mailid, {'folder':'outbox'}, context): - return {'type':'ir.actions.act_window_close' } - - def get_generated(self, cr, uid, ids=None, context=None): - if ids is None: - ids = [] - if context is None: - context = {} - logger = netsvc.Logger() - if context['src_rec_ids'] and len(context['src_rec_ids'])>1: - #Means there are multiple items selected for email. - mail_ids = self.save_to_mailbox(cr, uid, ids, context) - if mail_ids: - self.pool.get('email_template.mailbox').write(cr, uid, mail_ids, {'folder':'outbox'}, context) - logger.notifyChannel("email-template", netsvc.LOG_INFO, _("Emails for multiple items saved in outbox.")) - self.write(cr, uid, ids, { - 'generated':len(mail_ids), - 'state':'done' - }, context) - else: - raise osv.except_osv(_("Email Template"),_("Email sending failed for one or more objects.")) - return True - - def save_to_mailbox(self, cr, uid, ids, context=None): - def get_end_value(id, value): - if len(context['src_rec_ids']) > 1: # Multiple Mail: Gets value from the template - return self.get_value(cr, uid, template, value, context, id) - else: - return value - - if context is None: - context = {} - mail_ids = [] - template = self._get_template(cr, uid, context) - for id in context['src_rec_ids']: - screen_vals = self.read(cr, uid, ids[0], [],context) - account = self.pool.get('email_template.account').read(cr, uid, screen_vals['from'], context=context) - vals = { - 'email_from': tools.ustr(account['name']) + "<" + tools.ustr(account['email_id']) + ">", - 'email_to': get_end_value(id, screen_vals['to']), - 'email_cc': get_end_value(id, screen_vals['cc']), - 'email_bcc': get_end_value(id, screen_vals['bcc']), - 'subject': get_end_value(id, screen_vals['subject']), - 'body_text': get_end_value(id, screen_vals['body_text']), - 'body_html': get_end_value(id, screen_vals['body_html']), - 'account_id': screen_vals['from'], - 'state':'na', - 'mail_type':'multipart/alternative' #Options:'multipart/mixed','multipart/alternative','text/plain','text/html' - } - if screen_vals['signature']: - signature = self.pool.get('res.users').read(cr, uid, uid, ['signature'], context)['signature'] - if signature: - vals['body_text'] = tools.ustr(vals['body_text'] or '') + signature - vals['body_html'] = tools.ustr(vals['body_html'] or '') + signature - - attachment_ids = [] - - #Create partly the mail and later update attachments - mail_id = self.pool.get('email_template.mailbox').create(cr, uid, vals, context) - mail_ids.append(mail_id) - if template.report_template: - reportname = 'report.' + self.pool.get('ir.actions.report.xml').read(cr, uid, template.report_template.id, ['report_name'], context)['report_name'] - data = {} - data['model'] = self.pool.get('ir.model').browse(cr, uid, screen_vals['rel_model'], context).model - - # Ensure report is rendered using template's language - ctx = context.copy() - if template.lang: - ctx['lang'] = self.get_value(cr, uid, template, template.lang, context, id) - service = netsvc.LocalService(reportname) - (result, format) = service.create(cr, uid, [id], data, ctx) - attachment_id = self.pool.get('ir.attachment').create(cr, uid, { - 'name': _('%s (Email Attachment)') % tools.ustr(vals['subject']), - 'datas': base64.b64encode(result), - 'datas_fname': tools.ustr(get_end_value(id, screen_vals['report']) or _('Report')) + "." + format, - 'description': vals['body_text'] or _("No Description"), - 'res_model': 'email_template.mailbox', - 'res_id': mail_id - }, context) - attachment_ids.append( attachment_id ) - - # Add document attachments - for attachment_id in screen_vals.get('attachment_ids',[]): - new_id = self.pool.get('ir.attachment').copy(cr, uid, attachment_id, { - 'res_model': 'email_template.mailbox', - 'res_id': mail_id, - }, context) - attachment_ids.append( new_id ) - - if attachment_ids: - self.pool.get('email_template.mailbox').write(cr, uid, mail_id, { - 'attachments_ids': [[6, 0, attachment_ids]], - 'mail_type': 'multipart/mixed' - }, context) - - return mail_ids -email_template_send_wizard() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/wizard/email_template_send_wizard_view.xml b/addons/email_template/wizard/email_template_send_wizard_view.xml deleted file mode 100644 index a5ad037aee69243245957123e6f99f5945072c38..0000000000000000000000000000000000000000 --- a/addons/email_template/wizard/email_template_send_wizard_view.xml +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - - <record model="ir.ui.view" id="email_template_send_wizard_form"> - <field name="name">email_template.send.wizard.form</field> - <field name="model">email_template.send.wizard</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Send mail Wizard"> - <group col="4" colspan="4"> - <field name="rel_model" colspan="2" /> - <field name="from" required="1" colspan="2" /> - </group> - <group col="4" colspan="4"> - <group col="6" colspan="4"> - <field name="to" select="1" colspan="4" /> - <newline /> - <field name="cc" select="2" colspan="4" /> - <newline /> - <field name="bcc" select="2" colspan="4" /> - <newline /> - <field name="subject" select="2" colspan="4" attrs="{'required':[('state','=','single')]}" /> - <newline /> - <field name="report" colspan="4" /> - </group> - <separator string="" colspan="4" /> - <notebook colspan="4"> - <page string="Body (Plain Text)"> - <field name="body_text" select="2" colspan="4" nolabel="1" /> - </page> - <page string="Body (HTML)"> - <field name="body_html" select="2" colspan="4" nolabel="1" /> - </page> - <page string="Attachments"> - <label string="Add here all attachments of the current document you want to include in the Email." colspan="4"/> - <field name="attachment_ids" colspan="4" nolabel="1"/> - </page> - </notebook> - <field name="signature" colspan="4" /> - </group> - <group col="4" colspan="4" attrs="{'invisible':[('state','!=','single')]}"> - <button icon="gtk-apply" name="sav_to_drafts" string="Save in Drafts" type="object" /> - <button icon="gtk-ok" name="send_mail" string="Send now" type="object" /> - <button icon="gtk-cancel" special="cancel" string="Discard Mail" /> - </group> - <group col="4" colspan="4" attrs="{'invisible':[('state','=','single')]}"> - <label string="Tip: Multiple emails are sent in the same language (the first one is proposed). We suggest you send emails in groups according to language." colspan="4"/> - <field name="requested" /> - <field name="generated" /> - <button icon="gtk-ok" name="get_generated" string="Send all mails" type="object" states="multi" colspan="2" /> - <button icon="gtk-cancel" special="cancel" string="Discard Mail" colspan="2" states="multi" /> - </group> - <button icon="gtk-ok" special="cancel" string="Close" colspan="4" states="done" /> - <field name="state" /> - <newline /> - <label string="After clicking send all mails, mails will be sent to outbox and cleared in next Send/Recieve" colspan="4"/> - </form> - </field> - </record> - </data> -</openerp> - diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py new file mode 100644 index 0000000000000000000000000000000000000000..d40f9541b0112bc74943116fedfaa0b281a2bcff --- /dev/null +++ b/addons/email_template/wizard/mail_compose_message.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +import base64 + +from osv import osv +from osv import fields +from tools.translate import _ +import tools + + +def _reopen(self,res_id,model): + return {'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'view_type': 'form', + 'res_id': res_id, + 'res_model': self._name, + 'target': 'new', + + # save original model in context, otherwise + # it will be lost on the action's context switch + 'mail.compose.target.model': model, + } + +class mail_compose_message(osv.osv_memory): + _inherit = 'mail.compose.message' + + def _get_templates(self, cr, uid, context=None): + """ + Return Email Template of particular Model. + """ + if context is None: + context = {} + record_ids = [] + email_template= self.pool.get('email.template') + model = False + if context.get('message_id'): + mail_message = self.pool.get('mail.message') + message_data = mail_message.browse(cr, uid, int(context.get('message_id')), context) + model = message_data.model + elif context.get('mail.compose.target.model') or context.get('active_model'): + model = context.get('mail.compose.target.model', context.get('active_model')) + if model: + record_ids = email_template.search(cr, uid, [('model', '=', model)]) + return email_template.name_get(cr, uid, record_ids, context) + [(False,'')] + return [] + + _columns = { + 'use_template': fields.boolean('Use Template'), + 'template_id': fields.selection(_get_templates, 'Template'), + } + + def on_change_template(self, cr, uid, ids, use_template, template_id, email_from=None, email_to=None, context=None): + if context is None: + context = {} + values = {} + if template_id: + res_id = context.get('active_id', False) + if context.get('mail.compose.message.mode') == 'mass_mail': + # use the original template values - to be rendered when actually sent + # by super.send_mail() + values = self.pool.get('email.template').read(cr, uid, template_id, self.fields_get_keys(cr, uid), context) + else: + # render the mail as one-shot + values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context) + # retrofit generated attachments in the expected field format + if values['attachments']: + attachment = values.pop('attachments') + attachment_obj = self.pool.get('ir.attachment') + att_ids = [] + for fname, fcontent in attachment.iteritems(): + data_attach = { + 'name': fname, + 'datas': base64.b64encode(fcontent), + 'datas_fname': fname, + 'description': fname, + 'res_model' : self._name, + 'res_id' : ids[0] if ids else False + } + att_ids.append(attachment_obj.create(cr, uid, data_attach)) + values['attachment_ids'] = att_ids + else: + # restore defaults + values = self.default_get(cr, uid, self.fields_get_keys(cr, uid), context) + values.update(use_template=use_template, template_id=template_id) + + return {'value': values} + + + def template_toggle(self, cr, uid, ids, context=None): + for record in self.browse(cr, uid, ids, context=context): + had_template = record.use_template + record.write({'use_template': not(had_template)}) + if had_template: + # equivalent to choosing an empty template + onchange_defaults = self.on_change_template(cr, uid, record.id, not(had_template), + False, email_from=record.email_from, + email_to=record.email_to, context=context) + record.write(onchange_defaults['value']) + return _reopen(self, record.id, record.model) + + def save_as_template(self, cr, uid, ids, context=None): + if context is None: + context = {} + email_template = self.pool.get('email.template') + model_pool = self.pool.get('ir.model') + for record in self.browse(cr, uid, ids, context=context): + model = record.model or context.get('active_model') + model_ids = model_pool.search(cr, uid, [('model', '=', model)]) + model_id = model_ids and model_ids[0] or False + model_name = '' + if model_id: + model_name = model_pool.browse(cr, uid, model_id, context=context).name + template_name = "%s: %s" % (model_name, tools.ustr(record.subject)) + values = { + 'name': template_name, + 'email_from': record.email_from or False, + 'subject': record.subject or False, + 'body_text': record.body_text or False, + 'email_to': record.email_to or False, + 'email_cc': record.email_cc or False, + 'email_bcc': record.email_bcc or False, + 'reply_to': record.reply_to or False, + 'model_id': model_id or False, + 'attachment_ids': [(6, 0, [att.id for att in record.attachment_ids])] + } + template_id = email_template.create(cr, uid, values, context=context) + record.write({'template_id': template_id, + 'use_template': True}) + + # _reopen same wizard screen with new template preselected + return _reopen(self, record.id, model) + + # override the basic implementation + def render_template(self, cr, uid, template, model, res_id, context=None): + return self.pool.get('email.template').render_template(cr, uid, template, model, res_id, context=context) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/event/__openerp__.py b/addons/event/__openerp__.py index 680cf1e6572d5234c66f8562c36c47ab66aa999f..9a9acaf0fb3a5489440ae58dcb327d85ae93ec6e 100644 --- a/addons/event/__openerp__.py +++ b/addons/event/__openerp__.py @@ -29,7 +29,7 @@ Organization and management of Events. ====================================== -This module allow you +This module allows you * to manage your events and their registrations * to use emails to automatically confirm and send acknowledgements for any registration to an event * ... @@ -39,7 +39,7 @@ Note that: Association / Configuration / Types of Events """, 'author': 'OpenERP SA', - 'depends': ['crm', 'base_contact', 'account', 'marketing'], + 'depends': ['crm', 'base_contact', 'account', 'marketing', 'mail'], 'init_xml': [], 'update_xml': [ 'security/event_security.xml', diff --git a/addons/event/event.py b/addons/event/event.py index 0fc28c7c193d02388c2e2cef1ec34834a326f6c3..ba802ef1ffa60d2c24b665c541b07aaefce79025 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -24,8 +24,11 @@ import time from crm import crm from osv import fields, osv from tools.translate import _ -import tools import decimal_precision as dp +from crm import wizard + + +wizard.mail_compose_message.SUPPORTED_MODELS.append('event.registration') class event_type(osv.osv): """ Event Type """ @@ -280,7 +283,7 @@ class event_registration(osv.osv): """Event Registration""" _name= 'event.registration' _description = __doc__ - _inherit = 'mailgate.thread' + _inherit = 'mail.thread' def _amount_line(self, cr, uid, ids, field_name, arg, context=None): cur_obj = self.pool.get('res.currency') @@ -313,8 +316,8 @@ class event_registration(osv.osv): 'create_date': fields.datetime('Creation Date', readonly=True), 'write_date': fields.datetime('Write Date', readonly=True), 'description': fields.text('Description', states={'done': [('readonly', True)]}), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), - 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('model','=',_name)]), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + 'log_ids': fields.one2many('mail.message', 'res_id', 'Logs', domain=[('email_from', '=', False),('model','=',_name)]), 'date_deadline': fields.related('event_id','date_end', type='datetime', string="End Date", readonly=True), 'date': fields.related('event_id', 'date_begin', type='datetime', string="Start Date", readonly=True), 'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}), @@ -354,7 +357,7 @@ class event_registration(osv.osv): }) inv_id = inv_pool.create(cr, uid, val_invoice['value'], context=context) inv_pool.button_compute(cr, uid, [inv_id]) - self.history(cr, uid, [reg], _('Invoiced')) + self.message_append(cr, uid, [reg], _('Invoiced')) return inv_id def copy(self, cr, uid, id, default=None, context=None): @@ -429,7 +432,7 @@ class event_registration(osv.osv): """ res = self.write(cr, uid, ids, {'state': 'open'}, context=context) self.mail_user(cr, uid, ids) - self.history(cr, uid, ids, _('Open')) + self.message_append(cr, uid, ids, _('Open')) return res def do_close(self, cr, uid, ids, context=None): @@ -443,7 +446,7 @@ class event_registration(osv.osv): if invoice_id: values['invoice_id'] = invoice_id res = self.write(cr, uid, ids, values) - self.history(cr, uid, ids, msg) + self.message_append(cr, uid, ids, msg) return res def check_confirm(self, cr, uid, ids, context=None): @@ -514,42 +517,38 @@ class event_registration(osv.osv): """This Function Cancel Event Registration. """ registrations = self.browse(cr, uid, ids) - self.history(cr, uid, registrations, _('Cancel')) + self.message_append(cr, uid, registrations, _('Cancel')) return self.write(cr, uid, ids, {'state': 'cancel'}) def mail_user(self, cr, uid, ids, confirm=False, context=None): """ Send email to user """ - - for regestration in self.browse(cr, uid, ids, context=context): - src = regestration.event_id.reply_to or False + mail_message = self.pool.get('mail.message') + for registration in self.browse(cr, uid, ids, context=context): + src = registration.event_id.reply_to or False email_to = [] email_cc = [] - if regestration.email_from: - email_to = regestration.email_from - if regestration.email_cc: - email_cc += [regestration.email_cc] + if registration.email_from: + email_to = [registration.email_from] + if registration.email_cc: + email_cc += [registration.email_cc] if not (email_to or email_cc): continue subject = "" body = "" if confirm: - subject = _('Auto Confirmation: [%s] %s') %(regestration.id, regestration.name) - body = regestration.event_id.mail_confirm - elif regestration.event_id.mail_auto_confirm or regestration.event_id.mail_auto_registr: - if regestration.event_id.state in ['draft', 'fixed', 'open', 'confirm', 'running'] and regestration.event_id.mail_auto_registr: - subject = _('Auto Registration: [%s] %s') %(regestration.id, regestration.name) - body = regestration.event_id.mail_registr - if (regestration.event_id.state in ['confirm', 'running']) and regestration.event_id.mail_auto_confirm: - subject = _('Auto Confirmation: [%s] %s') %(regestration.id, regestration.name) - body = regestration.event_id.mail_confirm + subject = _('Auto Confirmation: [%s] %s') %(registration.id, registration.name) + body = registration.event_id.mail_confirm + elif registration.event_id.mail_auto_confirm or registration.event_id.mail_auto_registr: + if registration.event_id.state in ['draft', 'fixed', 'open', 'confirm', 'running'] and registration.event_id.mail_auto_registr: + subject = _('Auto Registration: [%s] %s') %(registration.id, registration.name) + body = registration.event_id.mail_registr + if (registration.event_id.state in ['confirm', 'running']) and registration.event_id.mail_auto_confirm: + subject = _('Auto Confirmation: [%s] %s') %(registration.id, registration.name) + body = registration.event_id.mail_confirm if subject or body: - tools.email_send(src, email_to, subject, body, email_cc=email_cc, openobject_id=regestration.id) - self.history(cr, uid, [regestration], subject, history = True, \ - email=email_to, details=body, \ - subject=subject, email_from=src, \ - email_cc=', '.join(email_cc)) + mail_message.schedule_with_attach(cr, uid, src, email_to, subject, body, model='event.registration', email_cc=email_cc, res_id=registration.id) return True diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index 6eafd8515591e6d2e490b8b320e0ff28b3febdf5..41c328d890802b0ee4bf3cb68803b2cb039dde18 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -391,12 +391,12 @@ <field name="message_ids" colspan="4" nolabel="1" mode="tree,form"> <tree string="History"> <field name="display_text" string="History Information"/> - <field name="history" invisible="1"/> - <button - string="Reply" attrs="{'invisible': [('history', '!=', True)]}" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'event.registration', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <field name="email_from" invisible="1"/> + <button + string="Reply" attrs="{'invisible': [('email_from', '=', False)]}" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action" /> </tree> <form string="History"> <group col="4" colspan="4"> @@ -404,20 +404,18 @@ <field name="date"/> <field name="email_to" widget="char" size="512"/> <field name="email_cc" widget="char" size="512"/> - <field name="name" colspan="4" widget="char" size="512"/> - <field name="history" invisible="1"/> + <field name="subject" colspan="4" widget="char" size="512"/> </group> <notebook colspan="4"> <page string="Details"> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <field name="description" colspan="4" nolabel="1" height="250"/> - <button colspan="4" - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'event.registration', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1" height="250"/> + <button colspan="4" string="Reply" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action"/> </group> - <group attrs="{'invisible': [('history', '=', True)]}"> + <group attrs="{'invisible': [('email_from', '!=', False)]}"> <field name="display_text" colspan="4" nolabel="1" height="250"/> </group> </page> @@ -427,14 +425,13 @@ </notebook> </form> </field> - <button string="Add Internal Note" + <button string="Add Internal Note" name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action" /> - <button string="Send New Email" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'event.registration'}" - icon="terp-mail-message-new" type="action" /> + <button string="Send New Email" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action"/> </page> </notebook> </form> diff --git a/addons/fetchmail/__openerp__.py b/addons/fetchmail/__openerp__.py index d2694fdb958d3bd557386765279cee3774b29acc..986c3bc12a13b5e93b08bc05704d38a96b57d6c0 100644 --- a/addons/fetchmail/__openerp__.py +++ b/addons/fetchmail/__openerp__.py @@ -21,19 +21,42 @@ ############################################################################## { - "name" : "Fetchmail Server", + "name" : "Automated Email Retriever", "version" : "1.0", - "depends" : ["base", 'mail_gateway'], + "depends" : ["base", 'mail'], "author" : "OpenERP SA", "category": 'Tools', "description": """ -Fetch email from POP / IMAP servers. -==================================== +Retrieve incoming email on POP / IMAP servers +============================================= - * Supports SSL - * Integrated with all modules - * Automatically receive email - * Email-based record operations (Add, Update) +Enter the parameters of your POP/IMAP account(s), and any incoming +emails on these accounts will be automatically downloaded into your OpenERP +system. All POP3/IMAP-compatible servers are supported, included those +that require an encrypted SSL/TLS connection. + +This can be used to easily create email-based workflows for many +email-enabled OpenERP documents, such as: + + * CRM Leads/Opportunities + * CRM Claims + * Project Issues + * Project Tasks + * Human Resource Recruitments (Applicants) + * etc. + +Just install the relevant application, and you can assign any of +these document types (Leads, Project Issues, etc.) to your incoming +email accounts. New emails will automatically spawn new documents +of the chosen type, so it's a snap to create a mailbox-to-OpenERP +integration. Even better: these documents directly act as mini +conversations synchronized by email. You can reply from within +OpenERP, and the answers will automatically be collected when +they come back, and attached to the same *conversation* document. + +For more specific needs, you may also assign custom-defined actions +(technically: Server Actions) to be triggered for each incoming +mail. """, 'website': 'http://www.openerp.com', 'init_xml': [], diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py index 9817f0d6633ac961f83a484b584e4190a0f8cdbf..b7460965c3b490b1a08c8b8f00dcc871d8a7e152 100644 --- a/addons/fetchmail/fetchmail.py +++ b/addons/fetchmail/fetchmail.py @@ -19,8 +19,8 @@ # ############################################################################## +import logging import time - from imaplib import IMAP4 from imaplib import IMAP4_SSL from poplib import POP3 @@ -29,222 +29,200 @@ from poplib import POP3_SSL import netsvc from osv import osv, fields import tools +from tools.translate import _ -logger = netsvc.Logger() - +logger = logging.getLogger('fetchmail') -class email_server(osv.osv): - - _name = 'email.server' +class fetchmail_server(osv.osv): + """Incoming POP/IMAP mail server account""" + _name = 'fetchmail.server' _description = "POP/IMAP Server" + _order = 'priority' _columns = { 'name':fields.char('Name', size=256, required=True, readonly=False), 'active':fields.boolean('Active', required=False), 'state':fields.selection([ ('draft', 'Not Confirmed'), - ('waiting', 'Waiting for Verification'), ('done', 'Confirmed'), ], 'State', select=True, readonly=True), - 'server' : fields.char('Server', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}), + 'server' : fields.char('Server Name', size=256, required=True, readonly=True, help="Hostname or IP of the mail server", states={'draft':[('readonly', False)]}), 'port' : fields.integer('Port', required=True, readonly=True, states={'draft':[('readonly', False)]}), 'type':fields.selection([ ('pop', 'POP Server'), ('imap', 'IMAP Server'), - ], 'Server Type', select=True, readonly=False), - 'is_ssl':fields.boolean('SSL ?', required=False), - 'attach':fields.boolean('Add Attachments ?', required=False, help="Fetches mail with attachments if true."), - 'date': fields.date('Date', readonly=True, states={'draft':[('readonly', False)]}), - 'user' : fields.char('User Name', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}), - 'password' : fields.char('Password', size=1024, invisible=True, required=True, readonly=True, states={'draft':[('readonly', False)]}), + ], 'Server Type', select=True, required=True, readonly=False), + 'is_ssl':fields.boolean('SSL/TLS', help="Connections are encrypted with SSL/TLS through a dedicated port (default: IMAPS=993, POP3S=995)"), + 'attach':fields.boolean('Keep Attachments', help="Whether attachments should be downloaded. " + "If not enabled, incoming emails will be stripped of any attachments before being processed"), + 'original':fields.boolean('Keep Original', help="Whether a full original copy of each email should be kept for reference" + "and attached to each processed message. This will usually double the size of your message database."), + 'date': fields.datetime('Last Fetch Date', readonly=True), + 'user' : fields.char('Username', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}), + 'password' : fields.char('Password', size=1024, required=True, readonly=True, states={'draft':[('readonly', False)]}), 'note': fields.text('Description'), - 'action_id':fields.many2one('ir.actions.server', 'Email Server Action', required=False, domain="[('state','=','email')]", help="An Email Server Action. It will be run whenever an e-mail is fetched from server."), - 'object_id': fields.many2one('ir.model', "Object To Create", required=True, help="Whenever an email arrives, it automatically creates the object of this type with all the information attached."), - 'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Priority between 0 to 10, select define the order of Processing"), - 'user_id':fields.many2one('res.users', 'User', required=False, help="This is the user that runs the cron"), - 'message_ids': fields.one2many('mailgate.message', 'server_id', 'Messages', readonly=True), + 'action_id':fields.many2one('ir.actions.server', 'Server Action', help="Optional custom server action to trigger for each incoming mail, " + "on the record that was created or updated by this mail"), + 'object_id': fields.many2one('ir.model', "Target document type", required=True, help="Process each incoming mail as part of a conversation " + "corresponding to this document type. This will create " + "new documents for new conversations, or attach follow-up " + "emails to the existing conversations (documents)."), + 'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Defines the order of processing, " + "lower values mean higher priority"), + 'message_ids': fields.one2many('mail.message', 'fetchmail_server_id', 'Messages', readonly=True), } _defaults = { - 'state': lambda *a: "draft", - 'active': lambda *a: True, - 'priority': lambda *a: 5, - 'date': lambda *a: time.strftime('%Y-%m-%d'), - 'user_id': lambda self, cr, uid, ctx: uid, + 'state': "draft", + 'active': True, + 'priority': 5, + 'attach': True, } - def check_duplicate(self, cr, uid, ids, context=None): - # RFC *-* Why this limitation? why not in SQL constraint? - vals = self.read(cr, uid, ids, ['user', 'password'], context=context)[0] - cr.execute("select count(id) from email_server where user=%s and password=%s", (vals['user'], vals['password'])) - res = cr.fetchone() - if res: - if res[0] > 1: - return False - return True - - def check_model(self, cr, uid, ids, context = None): - if context is None: - context = {} - current_rec = self.read(cr, uid, ids, context) - if current_rec: - current_rec = current_rec[0] - model_name = self.pool.get('ir.model').browse(cr, uid, current_rec.get('object_id')[0]).model - model = self.pool.get(model_name) - if hasattr(model, 'message_new'): - return True - return False - - _constraints = [ - (check_duplicate, 'Warning! Can\'t have duplicate server configuration!', ['user', 'password']), - (check_model, 'Warning! Record for selected Model can not be created\nPlease choose valid Model', ['object_id']) - ] - def onchange_server_type(self, cr, uid, ids, server_type=False, ssl=False): port = 0 if server_type == 'pop': port = ssl and 995 or 110 elif server_type == 'imap': port = ssl and 993 or 143 - return {'value':{'port':port}} def set_draft(self, cr, uid, ids, context=None): self.write(cr, uid, ids , {'state':'draft'}) return True - + + def connect(self, cr, uid, server_id, context=None): + if isinstance(server_id, (list,tuple)): + server_id = server_id[0] + server = self.browse(cr, uid, server_id, context) + if server.type == 'imap': + if server.is_ssl: + connection = IMAP4_SSL(server.server, int(server.port)) + else: + connection = IMAP4(server.server, int(server.port)) + connection.login(server.user, server.password) + elif server.type == 'pop': + if server.is_ssl: + connection = POP3_SSL(server.server, int(server.port)) + else: + connection = POP3(server.server, int(server.port)) + #TODO: use this to remove only unread messages + #connection.user("recent:"+server.user) + connection.user(server.user) + connection.pass_(server.password) + return connection + def button_confirm_login(self, cr, uid, ids, context=None): if context is None: context = {} for server in self.browse(cr, uid, ids, context=context): - logger.notifyChannel(server.type, netsvc.LOG_INFO, 'fetchmail start checking for new emails on %s' % (server.name)) - context.update({'server_id': server.id, 'server_type': server.type}) try: - if server.type == 'imap': - imap_server = None - if server.is_ssl: - imap_server = IMAP4_SSL(server.server, int(server.port)) - else: - imap_server = IMAP4(server.server, int(server.port)) - - imap_server.login(server.user, server.password) - ret_server = imap_server - - elif server.type == 'pop': - pop_server = None - if server.is_ssl: - pop_server = POP3_SSL(server.server, int(server.port)) - else: - pop_server = POP3(server.server, int(server.port)) - - #TODO: use this to remove only unread messages - #pop_server.user("recent:"+server.user) - pop_server.user(server.user) - pop_server.pass_(server.password) - ret_server = pop_server - - self.write(cr, uid, [server.id], {'state':'done'}) - if context.get('get_server',False): - return ret_server + connection = server.connect() + server.write({'state':'done'}) except Exception, e: - logger.notifyChannel(server.type, netsvc.LOG_WARNING, '%s' % (e)) - return True - - def button_fetch_mail(self, cr, uid, ids, context=None): - self.fetch_mail(cr, uid, ids, context=context) + logger.exception("Failed to connect to %s server %s", server.type, server.name) + raise osv.except_osv(_("Connection test failed!"), _("Here is what we got instead:\n %s") % tools.ustr(e)) + finally: + try: + if connection: + if server.type == 'imap': + connection.close() + elif server.type == 'pop': + connection.quit() + except Exception: + # ignored, just a consequence of the previous exception + pass return True def _fetch_mails(self, cr, uid, ids=False, context=None): if not ids: - ids = self.search(cr, uid, []) + ids = self.search(cr, uid, [('state','=','done')]) return self.fetch_mail(cr, uid, ids, context=context) def fetch_mail(self, cr, uid, ids, context=None): + """WARNING: meant for cron usage only - will commit() after each email!""" if context is None: context = {} - email_tool = self.pool.get('email.server.tools') + mail_thread = self.pool.get('mail.thread') action_pool = self.pool.get('ir.actions.server') - context.update({'get_server': True}) for server in self.browse(cr, uid, ids, context=context): + logger.info('start checking for new emails on %s server %s', server.type, server.name) + context.update({'fetchmail_server_id': server.id, 'server_type': server.type}) count = 0 - user = server.user_id.id or uid - try: - if server.type == 'imap': - imap_server = self.button_confirm_login(cr, uid, [server.id], context=context) + if server.type == 'imap': + try: + imap_server = server.connect() imap_server.select() result, data = imap_server.search(None, '(UNSEEN)') for num in data[0].split(): result, data = imap_server.fetch(num, '(RFC822)') - res_id = email_tool.process_email(cr, user, server.object_id.model, data[0][1], attach=server.attach, context=context) + res_id = mail_thread.message_process(cr, uid, server.object_id.model, data[0][1], + save_original=server.original, + strip_attachments=(not server.attach), + context=context) if res_id and server.action_id: - action_pool.run(cr, user, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]}) - + action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]}) imap_server.store(num, '+FLAGS', '\\Seen') + cr.commit() count += 1 - logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail fetch/process %s email(s) from %s' % (count, server.name)) - - imap_server.close() - imap_server.logout() - elif server.type == 'pop': - pop_server = self.button_confirm_login(cr, uid, [server.id], context=context) - pop_server.list() + logger.info("fetched/processed %s email(s) on %s server %s", count, server.type, server.name) + except Exception, e: + logger.exception("Failed to fetch mail from %s server %s", server.type, server.name) + finally: + if imap_server: + imap_server.close() + imap_server.logout() + elif server.type == 'pop': + try: + pop_server = server.connect() (numMsgs, totalSize) = pop_server.stat() + pop_server.list() for num in range(1, numMsgs + 1): (header, msges, octets) = pop_server.retr(num) msg = '\n'.join(msges) - res_id = email_tool.process_email(cr, user, server.object_id.model, msg, attach=server.attach, context=context) + res_id = mail_thread.message_process(cr, uid, server.object_id.model, + msg, + save_original=server.original, + strip_attachments=(not server.attach), + context=context) if res_id and server.action_id: - action_pool.run(cr, user, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]}) - + action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]}) pop_server.dele(num) - - pop_server.quit() - - logger.notifyChannel(server.type, netsvc.LOG_INFO, 'fetchmail fetch %s email(s) from %s' % (numMsgs, server.name)) - - except Exception, e: - logger.notifyChannel(server.type, netsvc.LOG_WARNING, '%s' % (tools.ustr(e))) - + cr.commit() + logger.info("fetched/processed %s email(s) on %s server %s", numMsgs, server.type, server.name) + except Exception, e: + logger.exception("Failed to fetch mail from %s server %s", server.type, server.name) + finally: + if pop_server: + pop_server.quit() + server.write({'date': time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)}) return True -email_server() - -class mailgate_message(osv.osv): - - _inherit = "mailgate.message" - +class mail_message(osv.osv): + _inherit = "mail.message" _columns = { - 'server_id': fields.many2one('email.server', "Mail Server", readonly=True, select=True), - 'server_type':fields.selection([ - ('pop', 'POP Server'), - ('imap', 'IMAP Server'), - ], 'Server Type', select=True, readonly=True), + 'fetchmail_server_id': fields.many2one('fetchmail.server', "Inbound Mail Server", + readonly=True, + select=True, + oldname='server_id'), } - _order = 'id desc' def create(self, cr, uid, values, context=None): if context is None: context={} - server_id = context.get('server_id',False) - server_type = context.get('server_type',False) - if server_id: - values['server_id'] = server_id - if server_type: - values['server_type'] = server_type - res = super(mailgate_message,self).create(cr, uid, values, context=context) + fetchmail_server_id = context.get('fetchmail_server_id') + if fetchmail_server_id: + values['fetchmail_server_id'] = fetchmail_server_id + res = super(mail_message,self).create(cr, uid, values, context=context) return res def write(self, cr, uid, ids, values, context=None): if context is None: context={} - server_id = context.get('server_id',False) - server_type = context.get('server_type',False) - if server_id: - values['server_id'] = server_id - if server_type: - values['server_type'] = server_type - res = super(mailgate_message,self).write(cr, uid, ids, values, context=context) + fetchmail_server_id = context.get('fetchmail_server_id') + if fetchmail_server_id: + values['fetchmail_server_id'] = server_id + res = super(mail_message,self).write(cr, uid, ids, values, context=context) return res -mailgate_message() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/fetchmail/fetchmail_data.xml b/addons/fetchmail/fetchmail_data.xml index ef40a76489f47f25408babe9fd40d32ac17783fc..d77aadc12de366d63513a3790f8a59dbc7bd60a9 100644 --- a/addons/fetchmail/fetchmail_data.xml +++ b/addons/fetchmail/fetchmail_data.xml @@ -6,10 +6,10 @@ <field name="interval_number">5</field> <field name="interval_type">minutes</field> <field name="numbercall">-1</field> - <field eval="False" name="doall"/> - <field eval="'email.server'" name="model"/> - <field eval="'_fetch_mails'" name="function"/> - <field eval="'()'" name="args"/> + <field name="doall" eval="False"/> + <field name="model">fetchmail.server</field> + <field name="function">_fetch_mails</field> + <field name="args">()</field> </record> </data> </openerp> diff --git a/addons/fetchmail/fetchmail_installer_view.xml b/addons/fetchmail/fetchmail_installer_view.xml index 34cfc686a8d2386fd5b1112f741bfbcda8cee0d6..911ad8101dab5e18d5525ac76f1e6594573f7747 100644 --- a/addons/fetchmail/fetchmail_installer_view.xml +++ b/addons/fetchmail/fetchmail_installer_view.xml @@ -1,9 +1,9 @@ <openerp> <data> <record model="ir.actions.act_window" id="view_email_server_form_installer"> - <field name="name">Setup Your Mail server</field> + <field name="name">Setup Incoming Mail Server (fetchmail)</field> <field name="type">ir.actions.act_window</field> - <field name="res_model">email.server</field> + <field name="res_model">fetchmail.server</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> <field name="view_id" ref="fetchmail.view_email_server_form"/> diff --git a/addons/fetchmail/fetchmail_view.xml b/addons/fetchmail/fetchmail_view.xml index 364d9bbe47f9c524ee845f0a92334bc22cffc478..05f62f4b9fb54b6ffcbbe0f0adca155b2376a8ed 100644 --- a/addons/fetchmail/fetchmail_view.xml +++ b/addons/fetchmail/fetchmail_view.xml @@ -1,44 +1,42 @@ <?xml version="1.0"?> <openerp> <data> - + <record model="ir.ui.view" id="view_email_server_tree"> - <field name="name">email.server.tree</field> - <field name="model">email.server</field> + <field name="name">fetchmail.server.list</field> + <field name="model">fetchmail.server</field> <field name="type">tree</field> <field name="arch" type="xml"> - <tree colors="blue:state == 'draft';black:state == 'waiting';gray:state == 'done'" string="POP/IMAP Servers"> + <tree colors="blue:state == 'draft'" string="POP/IMAP Servers"> <field name="name"/> <field name="type"/> - <field name="user"/> <field name="is_ssl"/> <field name="object_id"/> + <field name="date"/> <field name="message_ids" string="# of emails"/> <field name="state"/> </tree> </field> </record> - + <record model="ir.ui.view" id="view_email_server_form"> - <field name="name">email.server.form</field> - <field name="model">email.server</field> + <field name="name">fetchmail.server.form</field> + <field name="model">fetchmail.server</field> <field name="type">form</field> <field name="arch" type="xml"> - <form string="POP/IMAP Server"> + <form string="Incoming Mail Server"> <group col="6" colspan="4"> <field name="name" select="1" colspan="4"/> <field name="type" select="1" on_change="onchange_server_type(type, is_ssl)"/> <field name="date" select="1"/> - <field name="attach"/> - <field name="active" select="1"/> </group> <notebook colspan="4"> <page string="Server & Login"> - <group col="2" colspan="2"> + <group col="4" colspan="2"> <separator string="Server Information" colspan="2"/> - <field name="is_ssl" select="1" on_change="onchange_server_type(type, is_ssl)"/> - <field name="server" /> + <field name="server" colspan="4"/> <field name="port" /> + <field name="is_ssl" select="1" on_change="onchange_server_type(type, is_ssl)"/> </group> <group col="2" colspan="2"> <separator string="Login Information" colspan="2"/> @@ -46,35 +44,39 @@ <field name="password" password="True" /> </group> <group col="2" colspan="2"> - <separator string="Auto Reply?" colspan="2"/> - <field name="action_id"/> - <field name="user_id"/> - </group> - <group col="2" colspan="2"> - <separator string="Process Parameter" colspan="2"/> + <separator string="Automated Processing" colspan="2"/> <field name="object_id"/> - <field name="priority"/> + <field name="action_id"/> </group> <separator string="Description" colspan="4"/> <field name="note" colspan="4" nolabel="1"/> </page> + <page string="Advanced"> + <group colspan="2" col="2"> + <separator string="Advanced options" colspan="2"/> + <field name="priority"/> + <field name="attach"/> + <field name="original"/> + <field name="active" select="1"/> + </group> + </page> </notebook> <group col="6" colspan="4"> <field name="state" select="1"/> - <button string="Confirm" type="object" name="button_confirm_login" states="draft" icon="gtk-apply"/> - <button string="Fetch Emails" type="object" name="button_fetch_mail" states="done"/> - <button string="Set to Draft" type="object" name="set_draft" icon="gtk-convert"/> + <button string="Reset Confirmation" type="object" name="set_draft" icon="gtk-convert" states="done"/> + <button string="Test & Confirm" type="object" name="button_confirm_login" states="draft" icon="gtk-apply"/> + <button string="Fetch Now" type="object" name="fetch_mail" states="done" icon="gtk-network"/> </group> </form> </field> </record> - + <record model="ir.ui.view" id="view_email_server_search"> - <field name="name">email.server.search</field> - <field name="model">email.server</field> + <field name="name">fetchmail.server.search</field> + <field name="model">fetchmail.server</field> <field name="type">search</field> <field name="arch" type="xml"> - <search string="Search Email Servers"> + <search string="Search Incoming Mail Servers"> <filter string="IMAP" icon="terp-folder-green" domain="[('type','=','imap')]" help="Server type IMAP."/> <filter string="POP" icon="terp-folder-orange" domain="[('type','=','pop')]" help="Server type POP."/> <separator orientation="vertical"/> @@ -91,68 +93,52 @@ </search> </field> </record> - + <record model="ir.actions.act_window" id="action_email_server_tree"> - <field name="name">Email Servers</field> - <field name="res_model">email.server</field> + <field name="name">Incoming Mail Servers</field> + <field name="res_model">fetchmail.server</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> <field name="view_id" ref="view_email_server_tree"/> <field name="search_view_id" ref="view_email_server_search"/> </record> - <menuitem - parent="base.menu_mail_gateway" + <menuitem + parent="base.next_id_15" id="menu_action_fetchmail_server_tree" - action="action_email_server_tree" - name="Email Servers" + action="action_email_server_tree" + name="Incoming Mail Servers" + sequence="40" /> - - <record model="ir.ui.view" id="mailgate_message_tree_view"> - <field name="name">mailgate.message.tree</field> - <field name="model">mailgate.message</field> - <field name="type">tree</field> - <field name="inherit_id" ref="mail_gateway.view_mailgate_message_tree"/> - <field name="arch" type="xml"> - <field name="user_id" position="after"> - <field name="server_id" select="1"/> - <field name="ref_id"/> - </field> - </field> + + <record model="ir.ui.view" id="email_message_tree_view"> + <field name="name">mail.message.list.fetchmail</field> + <field name="model">mail.message</field> + <field name="type">tree</field> + <field name="inherit_id" ref="mail.view_email_message_tree"/> + <field name="arch" type="xml"> + <field name="user_id" position="after"> + <field name="fetchmail_server_id" select="1"/> + </field> + </field> </record> - - <record model="ir.ui.view" id="mailgate_message_search_view"> - <field name="name">mailgate.message.inherit.search</field> - <field name="model">mailgate.message</field> - <field name="type">search</field> - <field name="inherit_id" ref="mail_gateway.view_mailgate_message_search"/> - <field name="arch" type="xml"> - <xpath expr="/search/field[@name='name']" position="before"> - <filter string="Emails" name="emails" domain="[('server_id','!=',False)]" icon="terp-mail-message-new"/> - <separator orientation="vertical"/> - </xpath> - <xpath expr="/search/group/filter[@string='Thread']" position="before"> - <filter string="Mail Server" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'server_id'}"/> + + <record model="ir.ui.view" id="email_message_search_view"> + <field name="name">mail.message.inherit.search</field> + <field name="model">mail.message</field> + <field name="type">search</field> + <field name="inherit_id" ref="mail.view_email_message_search"/> + <field name="arch" type="xml"> + <xpath expr="/search/group/filter[@string='Thread']" position="before"> + <filter string="Mail Server" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'fetchmail_server_id'}"/> </xpath> - </field> + </field> </record> - - <record id="action_view_mail_message_emails" model="ir.actions.act_window"> - <field name="name">Messages</field> - <field name="res_model">mailgate.message</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="context">{'search_default_emails': 1}</field> - <field name="search_view_id" ref="mailgate_message_search_view"/> - </record> - - <menuitem id="base.menu_email_gateway_form" - parent="base.menu_mail_gateway" action="action_view_mail_message_emails" /> - <act_window - context="{'search_default_server_id': [active_id], 'default_server_id': active_id}" - id="act_server_history" name="Emails" - res_model="mailgate.message" src_model="email.server"/> - + <act_window + context="{'search_default_server_id': active_id, 'default_server_id': active_id}" + id="act_server_history" name="Messages" domain="[('email_from', '!=', False), ('fetchmail_server_id', '=', active_id)]" + res_model="mail.message" src_model="fetchmail.server"/> + </data> </openerp> diff --git a/addons/fetchmail/i18n/en_GB.po b/addons/fetchmail/i18n/en_GB.po index a0e83038e37b2c80bf41edef4af18734871f821b..d785f63b396eb4d7d30a9204a159306dd477f0c7 100644 --- a/addons/fetchmail/i18n/en_GB.po +++ b/addons/fetchmail/i18n/en_GB.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-01-11 11:15+0000\n" -"PO-Revision-Date: 2011-08-25 11:54+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"PO-Revision-Date: 2011-09-13 09:02+0000\n" +"Last-Translator: John Bradshaw <Unknown>\n" "Language-Team: English (United Kingdom) <en_GB@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: 2011-09-05 05:48+0000\n" -"X-Generator: Launchpad (build 13830)\n" +"X-Launchpad-Export-Date: 2011-09-14 04:41+0000\n" +"X-Generator: Launchpad (build 13921)\n" #. module: fetchmail #: constraint:email.server:0 @@ -105,7 +105,7 @@ msgstr "# of emails" #. module: fetchmail #: model:ir.actions.act_window,name:fetchmail.act_server_history msgid "Email History" -msgstr "" +msgstr "Email History" #. module: fetchmail #: field:email.server,user_id:0 @@ -161,7 +161,7 @@ msgstr "Mail gateway Message" #. module: fetchmail #: model:ir.actions.act_window,name:fetchmail.action_email_server_tree msgid "POP Servers" -msgstr "" +msgstr "POP Servers" #. module: fetchmail #: view:email.server:0 @@ -177,7 +177,7 @@ msgstr "Messages" #. module: fetchmail #: model:ir.ui.menu,name:fetchmail.menu_action_fetchmail_server_tree msgid "Fetchmail Services" -msgstr "" +msgstr "Fetchmail Services" #. module: fetchmail #: field:email.server,server:0 diff --git a/addons/fetchmail/security/ir.model.access.csv b/addons/fetchmail/security/ir.model.access.csv index 7aa6c84d22b995cc9b295e14218d813fce1af450..bc5572de82d7aac1f29ee34bf495e902c4af327f 100644 --- a/addons/fetchmail/security/ir.model.access.csv +++ b/addons/fetchmail/security/ir.model.access.csv @@ -1,2 +1,3 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_email_server","email.server","model_email_server",,1,1,1,1 +"access_fetchmail_server","fetchmail.server","model_fetchmail_server",,1,0,0,0 +"access_fetchmail_server","fetchmail.server","model_fetchmail_server","base.group_system",1,1,1,1 diff --git a/addons/google_map/i18n/mk.po b/addons/google_map/i18n/mk.po new file mode 100644 index 0000000000000000000000000000000000000000..5835b35357f93d79c56094e6707854ce59fb290f --- /dev/null +++ b/addons/google_map/i18n/mk.po @@ -0,0 +1,53 @@ +# Macedonian translation for openobject-addons +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2011-01-11 11:15+0000\n" +"PO-Revision-Date: 2011-09-14 08:11+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Macedonian <mk@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: 2011-09-15 04:47+0000\n" +"X-Generator: Launchpad (build 13921)\n" + +#. module: google_map +#: view:res.partner:0 +#: view:res.partner.address:0 +msgid "Map" +msgstr "Мапа" + +#. module: google_map +#: view:res.partner:0 +#: view:res.partner.address:0 +msgid "Street2 : " +msgstr "" + +#. module: google_map +#: model:ir.actions.wizard,name:google_map.wizard_google_map +msgid "Launch Google Map" +msgstr "Стартувај Google Map" + +#. module: google_map +#: model:ir.module.module,description:google_map.module_meta_information +msgid "" +"The module adds google map field in partner address\n" +"so that we can directly open google map from the\n" +"url widget." +msgstr "" + +#. module: google_map +#: model:ir.module.module,shortdesc:google_map.module_meta_information +msgid "Google Map" +msgstr "" + +#. module: google_map +#: model:ir.model,name:google_map.model_res_partner_address +msgid "Partner Addresses" +msgstr "ÐдреÑа на партнерот" diff --git a/addons/hr/hr.py b/addons/hr/hr.py index 43b66ac18d6b87d6e4d48612c60bdcd754efde58..1424d5e3378fb33cb39e98fc059637a3e821b89e 100644 --- a/addons/hr/hr.py +++ b/addons/hr/hr.py @@ -197,6 +197,7 @@ class hr_employee(osv.osv): _defaults = { 'active': 1, 'photo': _get_photo, + 'marital': 'single', } def _check_recursion(self, cr, uid, ids, context=None): diff --git a/addons/hr_contract/hr_contract_view.xml b/addons/hr_contract/hr_contract_view.xml index 2d2bdc5a1783ecf42af9d831fe17df81950a7b30..cfccd8912dc6de3e21aa4491d9caedfa5e69feea 100644 --- a/addons/hr_contract/hr_contract_view.xml +++ b/addons/hr_contract/hr_contract_view.xml @@ -75,8 +75,8 @@ <field name="type_id" widget="selection"/> </group> <notebook> - <page string="Information"> - <group col="2" colspan="2"> + <page string="Information" name="information"> + <group col="2" colspan="2" name="left_column"> <separator colspan="2" string="Duration"/> <field name="date_start" /> <field name="date_end" /> @@ -85,7 +85,7 @@ <field name="trial_date_start" /> <field name="trial_date_end" /> </group> - <group col="2" colspan="2"> + <group col="2" colspan="2" name="right_column"> <separator name="advantages" colspan="2" string="Advantages"/> <field name="advantages" nolabel="1" colspan="2"/> </group> diff --git a/addons/hr_evaluation/__openerp__.py b/addons/hr_evaluation/__openerp__.py index d2e0702f067c6217c8d9468a27af6fcbde901c53..adf1d9d69c47a65b1702f8b15e3b36681918a1e1 100644 --- a/addons/hr_evaluation/__openerp__.py +++ b/addons/hr_evaluation/__openerp__.py @@ -43,7 +43,7 @@ in the form of pdf file. Implements a dashboard for My Current Evaluations "data": [ "security/ir.model.access.csv", "security/hr_evaluation_security.xml", - "wizard/hr_evaluation_mail_view.xml", +# "wizard/hr_evaluation_mail_view.xml", "hr_evaluation_view.xml", "report/hr_evaluation_report_view.xml", "board_hr_evaluation_view.xml", diff --git a/addons/hr_evaluation/hr_evaluation.py b/addons/hr_evaluation/hr_evaluation.py index 0b0ebb92261621ba46dda4ef7a032f1d7cab862d..7634052704ca6c2e0a61746067af1c20d5f73b52 100644 --- a/addons/hr_evaluation/hr_evaluation.py +++ b/addons/hr_evaluation/hr_evaluation.py @@ -24,7 +24,6 @@ from datetime import datetime from dateutil.relativedelta import relativedelta from dateutil import parser from osv import fields, osv -import tools from tools.translate import _ class hr_evaluation_plan(osv.osv): @@ -193,6 +192,7 @@ class hr_evaluation(osv.osv): return {'value': {'plan_id':evaluation_plan_id}} def button_plan_in_progress(self, cr, uid, ids, context=None): + mail_message = self.pool.get('mail.message') hr_eval_inter_obj = self.pool.get('hr.evaluation.interview') if context is None: context = {} @@ -229,7 +229,7 @@ class hr_evaluation(osv.osv): sub = phase.email_subject dest = [child.work_email] if dest: - tools.email_send(evaluation.employee_id.work_email, dest, sub, body) + mail_message.schedule_with_attach(cr, uid, evaluation.employee_id.work_email, dest, sub, body, context=context) self.write(cr, uid, ids, {'state':'wait'}, context=context) return True diff --git a/addons/hr_evaluation/hr_evaluation_view.xml b/addons/hr_evaluation/hr_evaluation_view.xml index 84d2569320cd639b37daa723b60f7598456059ba..869d1f93994b428554e67e5eb4963876c1fc1a3c 100644 --- a/addons/hr_evaluation/hr_evaluation_view.xml +++ b/addons/hr_evaluation/hr_evaluation_view.xml @@ -306,6 +306,10 @@ <field name="response" readonly="1"/> <field name="user_to_review_id"/> <field name="user_id" string="Interviewer"/> + <button string="Send Reminder Email" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action" colspan="2" + states="waiting_answer"/> </group> <newline/> <separator string="State" colspan="4"/> @@ -329,6 +333,7 @@ <field name="response" readonly="1" invisible="True"/> <button name="%(survey.action_view_survey_question_message)d" string="Interview Question" type="action" states="waiting_answer,done,cancel" icon="gtk-execute" context="{'survey_id': survey_id, 'response_id': [response], 'response_no':0, 'active' : response, 'request' : True, 'object' : 'hr.evaluation.interview', 'cur_id' : active_id}" attrs="{'readonly':[('survey_id','=',False)]}"/> <button name="%(survey.survey_browse_response)d" string="Print Interview" type="action" states="done" icon="gtk-print" context="{'survey_id': survey_id, 'response_id' : [response], 'response_no':0}" attrs="{'readonly':[('response','=',False)]}" /> + <button name="%(mail.action_email_compose_message_wizard)d" string="Send Reminder Email" icon="terp-mail-message-new" type="action" colspan="2" states="waiting_answer"/> <field name="state"/> </tree> </field> @@ -403,8 +408,16 @@ <menuitem name="Interview Requests" parent="menu_eval_hr" id="menu_open_hr_evaluation_interview_requests" action="action_hr_evaluation_interview_tree"/> - <menuitem name="Evaluation Reminders" parent="menu_eval_hr" id="menu_eval_send_mail" - action="action_hr_evaluation_send_mail" sequence="45" groups="base.group_hr_manager"/> + + <!-- Email Compose message Action--> + <act_window + id="evaluation_reminders" name="Evaluation Reminders" + res_model="mail.compose.message" + src_model="hr.evaluation.interview" + view_type="form" view_mode="form" + target="new" multi="True" + key2="client_action_multi" + context="{'mail.compose.message.mode':'mass_mail'}"/> <!-- Evaluation Interviews Button on Employee Form --> <act_window diff --git a/addons/hr_evaluation/wizard/__init__.py b/addons/hr_evaluation/wizard/__init__.py index 1a855087bbc3f3098a814af338e49723f152ad2f..64406ac6b1e0ff7be1675bd4f2227b6c14f1d61a 100644 --- a/addons/hr_evaluation/wizard/__init__.py +++ b/addons/hr_evaluation/wizard/__init__.py @@ -19,6 +19,7 @@ # ############################################################################## -import hr_evaluation_mail +#import hr_evaluation_mail +import mail_compose_message # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_evaluation/wizard/hr_evaluation_mail.py b/addons/hr_evaluation/wizard/hr_evaluation_mail.py index 17ad7415cb52ba7b706765e5d92a481d5aeccdd5..35c54a4cca32f47f257649c772a26582d10e6bb2 100644 --- a/addons/hr_evaluation/wizard/hr_evaluation_mail.py +++ b/addons/hr_evaluation/wizard/hr_evaluation_mail.py @@ -23,21 +23,20 @@ import tools class hr_evaluation_reminder(osv.osv_memory): _name = "hr.evaluation.reminder" - _description = "Sends Reminders to employess to fill the evaluations" + _description = "Sends Reminders to employees to fill the evaluations" _columns = { 'evaluation_id': fields.many2one('hr.evaluation.interview', 'Interview', required=True) } def send_mail(self, cr, uid, ids, context=None): + mail_message = self.pool.get('mail.message') hr_evaluation_interview_obj = self.pool.get('hr.evaluation.interview') evaluation_data = self.read(cr, uid, ids, context=context)[0] current_interview = hr_evaluation_interview_obj.browse(cr, uid, evaluation_data.get('evaluation_id')) if current_interview.state == "waiting_answer" and current_interview.user_to_review_id.work_email : msg = " Hello %s, \n\n Kindly post your response for '%s' survey interview. \n\n Thanks," %(current_interview.user_to_review_id.name, current_interview.survey_id.title) - tools.email_send(tools.config['email_from'], [current_interview.user_to_review_id.work_email],\ - 'Reminder to fill up Survey', msg) + mail_message.schedule_with_attach(cr, uid, tools.config['email_from'], [current_interview.user_to_review_id.work_email],\ + 'Reminder to fill up Survey', msg, context=context) return {'type': 'ir.actions.act_window_close'} -hr_evaluation_reminder() - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_evaluation/wizard/mail_compose_message.py b/addons/hr_evaluation/wizard/mail_compose_message.py new file mode 100644 index 0000000000000000000000000000000000000000..7b4c1b691647e4884e85736e3ab08e95473d760d --- /dev/null +++ b/addons/hr_evaluation/wizard/mail_compose_message.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +from osv import osv +from osv import fields +import tools +from tools.translate import _ + +class mail_compose_message(osv.osv_memory): + _inherit = 'mail.compose.message' + + def get_value(self, cr, uid, model, resource_id, context=None): + ''' + To get values of the resource_id for the model + @param model: Object + @param resource_id: id of a record for which values to be read + + @return: Returns a dictionary + ''' + if context is None: + context = {} + result = super(mail_compose_message, self).get_value(cr, uid, model, resource_id, context=context) + if model == 'hr.evaluation.interview' and resource_id: + model_pool = self.pool.get(model) + record_data = model_pool.browse(cr, uid, resource_id, context) + if record_data.state == "waiting_answer": + msg = _("Hello %s, \n\n Kindly post your response for '%s' survey interview. \n\n Thanks,") %(record_data.user_to_review_id.name, record_data.survey_id.title) + result.update({ + 'email_from': tools.config.get('email_from',''), + 'email_to': record_data.user_to_review_id.work_email or False, + 'subject': _("Reminder to fill up Survey"), + 'body_text': msg, + 'res_id': resource_id, + 'model': model, + 'email_cc': False, + 'email_bcc': False, + 'reply_to': False, + }) + return result + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index 4ab71801a6754d10b93f6bd6cc5ffa17ac2098ba..68d607b0335a5773d051bdb0e798d827fe9d0ae9 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -276,7 +276,7 @@ class hr_expense_line(osv.osv): if product_id: product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) res['name'] = product.name - amount_unit = product.price_get('standard_price', context=context)[product.id] + amount_unit = product.price_get('standard_price')[product.id] res['unit_amount'] = amount_unit if not uom_id: res['uom_id'] = product.uom_id.id diff --git a/addons/hr_payroll/hr_payroll.py b/addons/hr_payroll/hr_payroll.py index cdf6549fedd6bb72dcc9e368165fd7269229a5ee..464c3d295a47da46a0a060e14d6c280c7912eea3 100644 --- a/addons/hr_payroll/hr_payroll.py +++ b/addons/hr_payroll/hr_payroll.py @@ -50,6 +50,7 @@ class hr_payroll_structure(osv.osv): 'company_id':fields.many2one('res.company', 'Company', required=True), 'note': fields.text('Description'), 'parent_id':fields.many2one('hr.payroll.structure', 'Parent'), + 'children_ids':fields.one2many('hr.payroll.structure', 'parent_id', 'Children'), } def _get_parent(self, cr, uid, context=None): @@ -131,6 +132,10 @@ class hr_contract(osv.osv): ], 'Scheduled Pay', select=True), } + _defaults = { + 'schedule_pay': 'monthly', + } + def get_all_structures(self, cr, uid, contract_ids, context=None): """ @param contract_ids: list of contracts @@ -175,6 +180,7 @@ class hr_salary_rule_category(osv.osv): 'name':fields.char('Name', size=64, required=True, readonly=False), 'code':fields.char('Code', size=64, required=True, readonly=False), 'parent_id':fields.many2one('hr.salary.rule.category', 'Parent', help="Linking a salary category to its parent is used only for the reporting purpose."), + 'children_ids': fields.one2many('hr.salary.rule.category', 'parent_id', 'Children'), 'note': fields.text('Description'), 'company_id':fields.many2one('res.company', 'Company', required=False), } diff --git a/addons/hr_payroll/hr_payroll_demo.xml b/addons/hr_payroll/hr_payroll_demo.xml index bcbecd63ffd4ae4ee87b1aa035312304fcd617b0..87deaef2f517c2de0915f054233770ac669187e8 100644 --- a/addons/hr_payroll/hr_payroll_demo.xml +++ b/addons/hr_payroll/hr_payroll_demo.xml @@ -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) * contract.wage * 0.01</field> + <field name="amount_python_compute">result = (inputs.SALEURO.amount + inputs.SALASIA.amount) * 0.01</field> </record> <!-- Rule Inputs --> @@ -144,7 +144,7 @@ <field name="date_end" eval="time.strftime('%Y')+'-12-31'"/> <field name="struct_id" ref="hr_payroll.structure_001"/> <field name="employee_id" ref="hr_employee_bonamy0"/> - <field name="notes">This is a First Contract</field> + <field name="notes">Default contract for marketing executives</field> <field eval="4000.0" name="wage"/> <field eval="5" name="working_days_per_week"/> </record> diff --git a/addons/hr_payroll/hr_payroll_view.xml b/addons/hr_payroll/hr_payroll_view.xml index 8bb56bb1200afbb84181ef865d808fe09ba9c932..a2523fcb7ba898163ccb7fdb6b57f00c05573827 100644 --- a/addons/hr_payroll/hr_payroll_view.xml +++ b/addons/hr_payroll/hr_payroll_view.xml @@ -58,7 +58,7 @@ <!-- End Contract View--> <!-- Salary structure --> - <record id="view_hr_employee_grade_tree" model="ir.ui.view"> + <record id="view_hr_payroll_structure_list_view" model="ir.ui.view"> <field name="name">hr.payroll.structure.tree</field> <field name="model">hr.payroll.structure</field> <field name="type">tree</field> @@ -71,6 +71,19 @@ </tree> </field> </record> + <record id="view_hr_payroll_structure_tree" model="ir.ui.view"> + <field name="name">hr.payroll.structure.tree</field> + <field name="model">hr.payroll.structure</field> + <field name="type">tree</field> + <field name="field_parent">children_ids</field> + <field name="arch" type="xml"> + <tree string="Salary Structure"> + <field name="name"/> + <field name="code"/> + <field name="company_id" groups="base.group_multi_company" widget="selection"/> + </tree> + </field> + </record> <record id="view_hr_payroll_structure_filter" model="ir.ui.view"> <field name="name">hr.payroll.structure.select</field> @@ -116,18 +129,32 @@ </field> </record> - <record id="action_view_hr_employee_grade_form" model="ir.actions.act_window"> + <record id="action_view_hr_payroll_structure_list_form" model="ir.actions.act_window"> <field name="name">Salary Structures</field> <field name="res_model">hr.payroll.structure</field> <field name="view_type">form</field> - <field name="view_id" ref="view_hr_employee_grade_tree"/> + <field name="view_id" ref="view_hr_payroll_structure_list_view"/> </record> <menuitem - id="menu_hr_employee_function" - action="action_view_hr_employee_grade_form" + id="menu_hr_payroll_structure_view" + action="action_view_hr_payroll_structure_list_form" parent="payroll_configure" sequence="1" /> + <record id="action_view_hr_payroll_structure_tree" model="ir.actions.act_window"> + <field name="name">Salary Structures Hierarchy</field> + <field name="res_model">hr.payroll.structure</field> + <field name="view_type">tree</field> + <field name="domain">[('parent_id','=',False)]</field> + <field name="view_id" ref="view_hr_payroll_structure_tree"/> + </record> + <menuitem + id="menu_hr_payroll_structure_tree" + action="action_view_hr_payroll_structure_tree" + parent="payroll_configure" + sequence="2" + icon="STOCK_INDENT" + /> <!-- End Salary structure --> <!-- Payslip Line --> @@ -415,6 +442,19 @@ </tree> </field> </record> + <record id="hr_salary_rule_category_tree_view" model="ir.ui.view"> + <field name="name">hr.salary.rule.category.tree.view</field> + <field name="model">hr.salary.rule.category</field> + <field name="type">tree</field> + <field name="field_parent">children_ids</field> + <field name="arch" type="xml"> + <tree string="Salary Rule Categories"> + <field name="name"/> + <field name="code"/> + <field name="parent_id" invisible="1"/> + </tree> + </field> + </record> <record id="view_hr_salary_rule_category_filter" model="ir.ui.view"> <field name="name">hr.salary.rule.category.select</field> @@ -441,6 +481,19 @@ parent="payroll_configure" sequence="11" /> + <record id="action_hr_salary_rule_category_tree_view" model="ir.actions.act_window"> + <field name="name">Salary Rule Categories Hierarchy</field> + <field name="res_model">hr.salary.rule.category</field> + <field name="view_type">tree</field> + <field name="view_id" ref="hr_salary_rule_category_tree_view"/> + </record> + <menuitem + id="menu_hr_salary_rule_category_tree_view" + action="action_hr_salary_rule_category_tree_view" + parent="payroll_configure" + sequence="12" + icon="STOCK_INDENT" + /> <!-- Contribution Register diff --git a/addons/hr_payroll_l10n_be/__init__.py b/addons/hr_payroll_l10n_be/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..61db034b52cf5e33722746cc52020f21aacb97fd --- /dev/null +++ b/addons/hr_payroll_l10n_be/__init__.py @@ -0,0 +1,23 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## +import hr_payroll_l10n_be +import hr_contract +import hr_payroll diff --git a/addons/hr_payroll_l10n_be/__openerp__.py b/addons/hr_payroll_l10n_be/__openerp__.py new file mode 100644 index 0000000000000000000000000000000000000000..b9f887f3ff83e8a939dde72fc6bb378dd5932086 --- /dev/null +++ b/addons/hr_payroll_l10n_be/__openerp__.py @@ -0,0 +1,53 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## +{ + 'name': 'hr_payroll_l10n_be', + 'category': 'payroll', + 'init_xml':[], + 'author': 'OpenERP', + 'depends': ['hr_payroll','hr_contract'], + 'version': '1.0', + 'description': """ +Belgian Payroll system. +======================= + + * Employee Details + * Employee Contracts + * Passport based Contract + * Allowances / Deductions + * Allow to configure Basic / Grows / Net Salary + * Employee Payslip + * Monthly Payroll Register + * Integrated with Holiday Management + * Salary Maj, ONSS, Precompte Professionnel, Child Allowance, ... + """, + + 'active': False, + 'demo_xml': [ + 'hr_payroll_l10n_be_demo.xml', + ], + 'update_xml':[ + 'hr_payroll_l10n_be_view.xml', + 'hr_payroll_l10n_be_data.xml', + 'data/hr.salary.rule.csv', + ], + 'installable': True +} diff --git a/addons/hr_payroll_l10n_be/data/hr.salary.rule.csv b/addons/hr_payroll_l10n_be/data/hr.salary.rule.csv new file mode 100644 index 0000000000000000000000000000000000000000..a73749674a9943d5092468ea1cdfbd9eb0696b89 --- /dev/null +++ b/addons/hr_payroll_l10n_be/data/hr.salary.rule.csv @@ -0,0 +1,1434 @@ +"id","amount_select","condition_range_min","condition_range_max","amount_percentage","amount_fix","name","category_id","sequence","code","parent_rule_id/id","condition_select","condition_range","amount_percentage_base" +1,"fix",0,480,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +2,"fix",480,495,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +3,"fix",495,510,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +4,"fix",510,525,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +5,"fix",525,540,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +6,"fix",540,555,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +7,"fix",555,570,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +8,"fix",570,585,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +9,"fix",585,600,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +10,"fix",600,615,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +11,"fix",615,630,,-1.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +12,"fix",630,645,,-5.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +13,"fix",645,660,,-8.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +14,"fix",660,675,,-12.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +15,"fix",675,690,,-16.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +16,"fix",690,705,,-19.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +17,"fix",705,720,,-23.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +18,"fix",720,735,,-26.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +19,"fix",735,750,,-30.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +20,"fix",750,765,,-34.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +21,"fix",765,780,,-37.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +22,"fix",780,795,,-41.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +23,"fix",795,810,,-44.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +24,"fix",810,825,,-48.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +25,"fix",825,840,,-52.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +26,"fix",840,855,,-55.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +27,"fix",855,870,,-60.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +28,"fix",870,885,,-64.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +29,"fix",885,900,,-68.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +30,"fix",900,915,,-73.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +31,"fix",915,930,,-78.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +32,"fix",930,945,,-82.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +33,"fix",945,960,,-87.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +34,"fix",960,975,,-91.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +35,"fix",975,990,,-96.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +36,"fix",990,1005,,-100.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +37,"fix",1005,1020,,-105.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +38,"fix",1020,1035,,-110.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +39,"fix",1035,1050,,-114.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +40,"fix",1050,1065,,-119.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +41,"fix",1065,1080,,-123.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +42,"fix",1080,1095,,-128.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +43,"fix",1095,1110,,-133.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +44,"fix",1110,1125,,-139.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +45,"fix",1125,1140,,-145.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +46,"fix",1140,1155,,-151.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +47,"fix",1155,1170,,-157.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +48,"fix",1170,1185,,-163.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +49,"fix",1185,1200,,-169.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +50,"fix",1200,1215,,-175.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +51,"fix",1215,1230,,-181.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +52,"fix",1230,1245,,-188.03,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +53,"fix",1245,1260,,-194.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +54,"fix",1260,1275,,-200.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +55,"fix",1275,1290,,-206.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +56,"fix",1290,1305,,-212.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +57,"fix",1305,1320,,-218.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +58,"fix",1320,1335,,-224.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +59,"fix",1335,1350,,-230.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +60,"fix",1350,1365,,-236.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +61,"fix",1365,1380,,-242.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +62,"fix",1380,1395,,-249.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +63,"fix",1395,1410,,-255.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +64,"fix",1410,1425,,-261.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +65,"fix",1425,1440,,-267.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +66,"fix",1440,1455,,-273.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +67,"fix",1455,1470,,-279.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +68,"fix",1470,1485,,-285.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +69,"fix",1485,1500,,-291.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +70,"fix",1500,1515,,-298.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +71,"fix",1515,1530,,-304.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +72,"fix",1530,1545,,-310.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +73,"fix",1545,1560,,-317.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +74,"fix",1560,1575,,-324.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +75,"fix",1575,1590,,-331.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +76,"fix",1590,1605,,-338.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +77,"fix",1605,1620,,-345.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +78,"fix",1620,1635,,-352.95,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +79,"fix",1635,1650,,-359.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +80,"fix",1650,1665,,-366.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +81,"fix",1665,1680,,-373.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +82,"fix",1680,1695,,-380.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +83,"fix",1695,1710,,-387.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +84,"fix",1710,1725,,-394.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +85,"fix",1725,1740,,-401.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +86,"fix",1740,1755,,-409,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +87,"fix",1755,1770,,-416,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +88,"fix",1770,1785,,-423.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +89,"fix",1785,1800,,-430.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +90,"fix",1800,1815,,-437.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +91,"fix",1815,1830,,-444.03,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +92,"fix",1830,1845,,-451.03,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +93,"fix",1845,1860,,-458.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +94,"fix",1860,1875,,-465.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +95,"fix",1875,1890,,-472.05,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +96,"fix",1890,1905,,-479.05,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +97,"fix",1905,1920,,-486.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +98,"fix",1920,1935,,-493.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +99,"fix",1935,1950,,-500.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +100,"fix",1950,1965,,-507.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +101,"fix",1965,1980,,-514.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +102,"fix",1980,1995,,-521.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +103,"fix",1995,2010,,-528.1,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +104,"fix",2010,2025,,-535.1,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +105,"fix",2025,2040,,-542.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +106,"fix",2040,2055,,-549.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +107,"fix",2055,2070,,-556.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +108,"fix",2070,2085,,-563.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +109,"fix",2085,2100,,-570.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +110,"fix",2100,2115,,-577.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +111,"fix",2115,2130,,-584.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +112,"fix",2130,2145,,-591.15,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +113,"fix",2145,2160,,-598.15,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +114,"fix",2160,2175,,-605.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +115,"fix",2175,2190,,-612.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +116,"fix",2190,2205,,-619.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +117,"fix",2205,2220,,-626.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +118,"fix",2220,2235,,-633.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +119,"fix",2235,2250,,-640.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +120,"fix",2250,2265,,-647.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +121,"fix",2265,2280,,-654.2,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +122,"fix",2280,2295,,-661.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +123,"fix",2295,2310,,-668.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +124,"fix",2310,2325,,-675.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +125,"fix",2325,2340,,-682.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +126,"fix",2340,2355,,-689.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +127,"fix",2355,2370,,-696.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +128,"fix",2370,2385,,-703.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +129,"fix",2385,2400,,-710.25,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +130,"fix",2400,2415,,-717.25,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +131,"fix",2415,2430,,-724.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +132,"fix",2430,2445,,-731.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +133,"fix",2445,2460,,-738.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +134,"fix",2460,2475,,-745.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +135,"fix",2475,2490,,-752.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +136,"fix",2490,2505,,-759.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +137,"fix",2505,2520,,-766.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +138,"fix",2520,2535,,-773.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +139,"fix",2535,2550,,-780.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +140,"fix",2550,2565,,-787.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +141,"fix",2565,2580,,-794.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +142,"fix",2580,2595,,-801.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +143,"fix",2595,2610,,-808.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +144,"fix",2610,2625,,-815.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +145,"fix",2625,2640,,-822.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +146,"fix",2640,2655,,-829.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +147,"fix",2655,2670,,-836.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +148,"fix",2670,2685,,-843.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +149,"fix",2685,2700,,-850.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +150,"fix",2700,2715,,-857.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +151,"fix",2715,2730,,-864.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +152,"fix",2730,2745,,-871.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +153,"fix",2745,2760,,-878.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +154,"fix",2760,2775,,-885.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +155,"fix",2775,2790,,-892.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +156,"fix",2790,2805,,-899.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +157,"fix",2805,2820,,-906.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +158,"fix",2820,2835,,-913.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +159,"fix",2835,2850,,-920.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +160,"fix",2850,2865,,-927.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +161,"fix",2865,2880,,-934.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +162,"fix",2880,2895,,-941.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +163,"fix",2895,2910,,-948.45,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +164,"fix",2910,2925,,-955.45,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +165,"fix",2925,2940,,-962.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +166,"fix",2940,2955,,-969.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +167,"fix",2955,2970,,-976.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +168,"fix",2970,2985,,-983.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +169,"fix",2985,3000,,-990.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +170,"fix",3000,3015,,-997.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +171,"fix",3015,3030,,-1004.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +172,"fix",3030,3045,,-1011.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +173,"fix",3045,3060,,-1018.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +174,"fix",3060,3075,,-1025.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +175,"fix",3075,3090,,-1032.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +176,"fix",3090,3105,,-1039.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +177,"fix",3105,3120,,-1046.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +178,"fix",3120,3135,,-1053.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +179,"fix",3135,3150,,-1060.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +180,"fix",3150,3165,,-1067.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +181,"fix",3165,3180,,-1074.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +182,"fix",3180,3195,,-1081.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +183,"fix",3195,3210,,-1089.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +184,"fix",3210,3225,,-1097.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +185,"fix",3225,3240,,-1105.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +186,"fix",3240,3255,,-1113.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +187,"fix",3255,3270,,-1120.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +188,"fix",3270,3285,,-1128.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +189,"fix",3285,3300,,-1136.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +190,"fix",3300,3315,,-1144.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +191,"fix",3315,3330,,-1152.05,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +192,"fix",3330,3345,,-1159.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +193,"fix",3345,3360,,-1167.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +194,"fix",3360,3375,,-1175.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +195,"fix",3375,3390,,-1183.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +196,"fix",3390,3405,,-1190.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +197,"fix",3405,3420,,-1198.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +198,"fix",3420,3435,,-1206.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +199,"fix",3435,3450,,-1214.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +200,"fix",3450,3465,,-1222.1,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +201,"fix",3465,3480,,-1229.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +202,"fix",3480,3495,,-1237.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +203,"fix",3495,3510,,-1245.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +204,"fix",3510,3525,,-1253.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +205,"fix",3525,3540,,-1261.03,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +206,"fix",3540,3555,,-1268.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +207,"fix",3555,3570,,-1276.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +208,"fix",3570,3585,,-1284.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +209,"fix",3585,3600,,-1292.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +210,"fix",3600,3615,,-1299.95,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +211,"fix",3615,3630,,-1307.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +212,"fix",3630,3645,,-1315.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +213,"fix",3645,3660,,-1323.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +214,"fix",3660,3675,,-1331.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +215,"fix",3675,3690,,-1338.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +216,"fix",3690,3705,,-1346.65,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +217,"fix",3705,3720,,-1354.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +218,"fix",3720,3735,,-1362.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +219,"fix",3735,3750,,-1370.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +220,"fix",3750,3765,,-1377.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +221,"fix",3765,3780,,-1385.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +222,"fix",3780,3795,,-1393.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +223,"fix",3795,3810,,-1401.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +224,"fix",3810,3825,,-1408.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +225,"fix",3825,3840,,-1416.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +226,"fix",3840,3855,,-1424.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +227,"fix",3855,3870,,-1432.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +228,"fix",3870,3885,,-1440.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +229,"fix",3885,3900,,-1447.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +230,"fix",3900,3915,,-1455.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +231,"fix",3915,3930,,-1463.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +232,"fix",3930,3945,,-1471.2,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +233,"fix",3945,3960,,-1478.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +234,"fix",3960,3975,,-1486.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +235,"fix",3975,3990,,-1494.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +236,"fix",3990,4005,,-1502.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +237,"fix",4005,4020,,-1510.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +238,"fix",4020,4035,,-1517.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +239,"fix",4035,4050,,-1525.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +240,"fix",4050,4065,,-1533.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +241,"fix",4065,4080,,-1541.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +242,"fix",4080,4095,,-1549.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +243,"fix",4095,4110,,-1556.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +244,"fix",4110,4125,,-1564.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +245,"fix",4125,4140,,-1572.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +246,"fix",4140,4155,,-1580.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +247,"fix",4155,4170,,-1587.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +248,"fix",4170,4185,,-1595.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +249,"fix",4185,4200,,-1603.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +250,"fix",4200,4215,,-1611.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +251,"fix",4215,4230,,-1619.1,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +252,"fix",4230,4245,,-1626.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +253,"fix",4245,4260,,-1634.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +254,"fix",4260,4275,,-1642.45,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +255,"fix",4275,4290,,-1650.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +256,"fix",4290,4305,,-1658.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +257,"fix",4305,4320,,-1665.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +258,"fix",4320,4335,,-1673.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +259,"fix",4335,4350,,-1681.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +260,"fix",4350,4365,,-1689.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +261,"fix",4365,4380,,-1696.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +262,"fix",4380,4395,,-1704.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +263,"fix",4395,4410,,-1712.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +264,"fix",4410,4425,,-1720.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +265,"fix",4425,4440,,-1728.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +266,"fix",4440,4455,,-1735.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +267,"fix",4455,4470,,-1743.65,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +268,"fix",4470,4485,,-1751.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +269,"fix",4485,4500,,-1759.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +270,"fix",4500,4515,,-1767,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +271,"fix",4515,4530,,-1774.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +272,"fix",4530,4545,,-1782.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +273,"fix",4545,4560,,-1790.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +274,"fix",4560,4575,,-1798.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +275,"fix",4575,4590,,-1805.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +276,"fix",4590,4605,,-1813.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +277,"fix",4605,4620,,-1821.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +278,"fix",4620,4635,,-1829.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +279,"fix",4635,4650,,-1837.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +280,"fix",4650,4665,,-1844.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +281,"fix",4665,4680,,-1852.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +282,"fix",4680,4695,,-1860.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +283,"fix",4695,4710,,-1868.2,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +284,"fix",4710,4725,,-1875.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +285,"fix",4725,4740,,-1883.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +286,"fix",4740,4755,,-1891.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +287,"fix",4755,4770,,-1899.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +288,"fix",4770,4785,,-1907.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +289,"fix",4785,4800,,-1914.9,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +290,"fix",4800,4815,,-1922.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +291,"fix",4815,4830,,-1930.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +292,"fix",4830,4845,,-1938.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +293,"fix",4845,4860,,-1946.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +294,"fix",4860,4875,,-1953.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +295,"fix",4875,4890,,-1961.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +296,"fix",4890,4905,,-1969.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +297,"fix",4905,4920,,-1977.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +298,"fix",4920,4935,,-1984.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +299,"fix",4935,4950,,-1992.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +300,"fix",4950,4965,,-2000.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +301,"fix",4965,4980,,-2008.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +302,"fix",4980,4995,,-2016.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +303,"fix",4995,5010,,-2024.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +304,"fix",5010,5025,,-2032.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +305,"fix",5025,5040,,-2040.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +306,"fix",5040,5055,,-2048.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +307,"fix",5055,5070,,-2056.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +308,"fix",5070,5085,,-2064.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +309,"fix",5085,5100,,-2072.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +310,"fix",5100,5115,,-2080.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +311,"fix",5115,5130,,-2088.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +312,"fix",5130,5145,,-2096.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +313,"fix",5145,5160,,-2104.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +314,"fix",5160,5175,,-2112.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +315,"fix",5175,5190,,-2120.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +316,"fix",5190,5205,,-2128.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +317,"fix",5205,5220,,-2136.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +318,"fix",5220,5235,,-2144.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +319,"fix",5235,5250,,-2152.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +320,"fix",5250,5265,,-2160.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +321,"fix",5265,5280,,-2168.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +322,"fix",5280,5295,,-2176.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +323,"fix",5295,5310,,-2184.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +324,"fix",5310,5325,,-2192.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +325,"fix",5325,5340,,-2200.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +326,"fix",5340,5355,,-2208.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +327,"fix",5355,5370,,-2216.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +328,"fix",5370,5385,,-2224.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +329,"fix",5385,5400,,-2232.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +330,"fix",5400,5415,,-2240.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +331,"fix",5415,5430,,-2248.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +332,"fix",5430,5445,,-2256.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +333,"fix",5445,5460,,-2264.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +334,"fix",5460,5475,,-2272.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +335,"fix",5475,5490,,-2280.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +336,"fix",5490,5505,,-2288.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +337,"fix",5505,5520,,-2297.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +338,"fix",5520,5535,,-2305.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +339,"fix",5535,5550,,-2313.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +340,"fix",5550,5565,,-2321.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +341,"fix",5565,5580,,-2329.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +342,"fix",5580,5595,,-2337.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +343,"fix",5595,5610,,-2345.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +344,"fix",5610,5625,,-2353.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +345,"fix",5625,5640,,-2361.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +346,"fix",5640,5655,,-2369.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +347,"fix",5655,5670,,-2377.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +348,"fix",5670,5685,,-2385.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +349,"fix",5685,5700,,-2393.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +350,"fix",5700,5715,,-2401.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +351,"fix",5715,5730,,-2409.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +352,"fix",5730,5745,,-2417.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +353,"fix",5745,5760,,-2425.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +354,"fix",5760,5775,,-2433.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +355,"fix",5775,5790,,-2441.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +356,"fix",5790,5805,,-2449.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +357,"fix",5805,5820,,-2457.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +358,"fix",5820,5835,,-2465.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +359,"fix",5835,5850,,-2473.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +360,"fix",5850,5865,,-2481.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +361,"fix",5865,5880,,-2489.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +362,"fix",5880,5895,,-2497.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +363,"fix",5895,5910,,-2505.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +364,"fix",5910,5925,,-2513.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +365,"fix",5925,5940,,-2521.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +366,"fix",5940,5955,,-2529.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +367,"fix",5955,5970,,-2537.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +368,"fix",5970,5985,,-2545.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +369,"fix",5985,6000,,-2553.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +370,"fix",6000,6015,,-2561.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +371,"fix",6015,6030,,-2569.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +372,"fix",6030,6045,,-2577.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +373,"fix",6045,6060,,-2585.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +374,"fix",6060,6075,,-2593.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +375,"fix",6075,6090,,-2601.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +376,"fix",6090,6105,,-2609.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +377,"fix",6105,6120,,-2618.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +378,"fix",6120,6135,,-2626.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +379,"fix",6135,6150,,-2634.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +380,"fix",6150,6165,,-2642.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +381,"fix",6165,6180,,-2650.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +382,"fix",6180,6195,,-2658.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +383,"fix",6195,6210,,-2666.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +384,"fix",6210,6225,,-2674.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +385,"fix",6225,6240,,-2682.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +386,"fix",6240,6255,,-2690.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +387,"fix",6255,6270,,-2698.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +388,"fix",6270,6285,,-2706.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +389,"fix",6285,6300,,-2714.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +390,"fix",6300,6315,,-2722.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +391,"fix",6315,6330,,-2730.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +392,"fix",6330,6345,,-2738.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +393,"fix",6345,6360,,-2746.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +394,"fix",6360,6375,,-2754.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +395,"fix",6375,6390,,-2762.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +396,"fix",6390,6405,,-2770.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +397,"fix",6405,6420,,-2778.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +398,"fix",6420,6435,,-2786.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +399,"fix",6435,6450,,-2794.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +400,"fix",6450,6465,,-2802.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +401,"fix",6465,6480,,-2810.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +402,"fix",6480,6495,,-2818.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +403,"fix",6495,6510,,-2826.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +404,"fix",6510,6525,,-2834.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +405,"fix",6525,6540,,-2842.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +406,"fix",6540,6555,,-2850.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +407,"fix",6555,6570,,-2858.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +408,"fix",6570,6585,,-2866.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +409,"fix",6585,6600,,-2874.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +410,"fix",6600,6615,,-2882.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +411,"fix",6615,6630,,-2890.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +412,"fix",6630,6645,,-2898.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +413,"fix",6645,6660,,-2906.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +414,"fix",6660,6675,,-2914.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +415,"fix",6675,6690,,-2922.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +416,"fix",6690,6705,,-2930.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +417,"fix",6705,6720,,-2939.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +418,"fix",6720,6735,,-2947.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +419,"fix",6735,6750,,-2955.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +420,"fix",6750,6765,,-2963.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +421,"fix",6765,6780,,-2971.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +422,"fix",6780,6795,,-2979.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +423,"fix",6795,6810,,-2987.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +424,"fix",6810,6825,,-2995.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +425,"fix",6825,6840,,-3003.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +426,"fix",6840,6855,,-3011.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +427,"fix",6855,6870,,-3019.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +428,"fix",6870,6885,,-3027.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +429,"fix",6885,6900,,-3035.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +430,"fix",6900,6915,,-3043.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +431,"fix",6915,6930,,-3051.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +432,"fix",6930,6945,,-3059.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +433,"fix",6945,6960,,-3067.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +434,"fix",6960,6975,,-3075.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +435,"fix",6975,6990,,-3083.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +436,"fix",6990,7005,,-3091.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +437,"fix",7005,7020,,-3099.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +438,"fix",7020,7035,,-3107.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +439,"fix",7035,7050,,-3115.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +440,"fix",7050,7065,,-3123.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +441,"fix",7065,7080,,-3131.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +442,"fix",7080,7095,,-3139.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +443,"fix",7095,7110,,-3147.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +444,"fix",7110,7125,,-3155.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +445,"fix",7125,7140,,-3163.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +446,"fix",7140,7155,,-3171.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +447,"fix",7155,7170,,-3179.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +448,"fix",7170,7185,,-3187.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +449,"fix",7185,7200,,-3195.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +450,"fix",7200,7215,,-3203.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +451,"fix",7215,7230,,-3211.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +452,"fix",7230,7245,,-3219.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +453,"fix",7245,7260,,-3227.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +454,"fix",7260,7275,,-3235.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +455,"fix",7275,7290,,-3243.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +456,"fix",7290,7305,,-3251.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +457,"fix",7305,7320,,-3260.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +458,"fix",7320,7335,,-3268.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +459,"fix",7335,7350,,-3276.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +460,"fix",7350,7365,,-3284.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +461,"fix",7365,7380,,-3292.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +462,"fix",7380,7395,,-3300.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +463,"fix",7395,7410,,-3308.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +464,"fix",7410,7425,,-3316.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +465,"fix",7425,7440,,-3324.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +466,"fix",7440,7455,,-3332.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +467,"fix",7455,7470,,-3340.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +468,"fix",7470,7485,,-3348.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +469,"fix",7485,7500,,-3356.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +470,"fix",7500,7500,,-3364.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_bareme","range","GROSS", +,,,,,,,,,,,,, +471,"fix",0,960,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +472,"fix",960,975,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +473,"fix",975,990,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +474,"fix",990,1005,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +475,"fix",1005,1020,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +476,"fix",1020,1035,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +477,"fix",1035,1050,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +478,"fix",1050,1065,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +479,"fix",1065,1080,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +480,"fix",1080,1095,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +481,"fix",1095,1110,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +482,"fix",1110,1125,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +483,"fix",1125,1140,,-3.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +484,"fix",1140,1155,,-7.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +485,"fix",1155,1170,,-11.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +486,"fix",1170,1185,,-15.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +487,"fix",1185,1200,,-20.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +488,"fix",1200,1215,,-24.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +489,"fix",1215,1230,,-28.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +490,"fix",1230,1245,,-33.05,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +491,"fix",1245,1260,,-37.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +492,"fix",1260,1275,,-41.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +493,"fix",1275,1290,,-46.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +494,"fix",1290,1305,,-50.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +495,"fix",1305,1320,,-54.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +496,"fix",1320,1335,,-59.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +497,"fix",1335,1350,,-63.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +498,"fix",1350,1365,,-67.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +499,"fix",1365,1380,,-72.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +500,"fix",1380,1395,,-76.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +501,"fix",1395,1410,,-80.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +502,"fix",1410,1425,,-85.2,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +503,"fix",1425,1440,,-89.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +504,"fix",1440,1455,,-93.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +505,"fix",1455,1470,,-98.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +506,"fix",1470,1485,,-102.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +507,"fix",1485,1500,,-107.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +508,"fix",1500,1515,,-111.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +509,"fix",1515,1530,,-116.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +510,"fix",1530,1545,,-122.25,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +511,"fix",1545,1560,,-127.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +512,"fix",1560,1575,,-133.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +513,"fix",1575,1590,,-138.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +514,"fix",1590,1605,,-144.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +515,"fix",1605,1620,,-149.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +516,"fix",1620,1635,,-155.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +517,"fix",1635,1650,,-160.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +518,"fix",1650,1665,,-166.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +519,"fix",1665,1680,,-171.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +520,"fix",1680,1695,,-177.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +521,"fix",1695,1710,,-183.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +522,"fix",1710,1725,,-188.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +523,"fix",1725,1740,,-194.1,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +524,"fix",1740,1755,,-199.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +525,"fix",1755,1770,,-205.15,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +526,"fix",1770,1785,,-210.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +527,"fix",1785,1800,,-216.2,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +528,"fix",1800,1815,,-221.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +529,"fix",1815,1830,,-227.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +530,"fix",1830,1845,,-232.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +531,"fix",1845,1860,,-238.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +532,"fix",1860,1875,,-243.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +533,"fix",1875,1890,,-249.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +534,"fix",1890,1905,,-254.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +535,"fix",1905,1920,,-260.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +536,"fix",1920,1935,,-265.95,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +537,"fix",1935,1950,,-271.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +538,"fix",1950,1965,,-277,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +539,"fix",1965,1980,,-282.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +540,"fix",1980,1995,,-288.05,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +541,"fix",1995,2010,,-293.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +542,"fix",2010,2025,,-299.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +543,"fix",2025,2040,,-304.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +544,"fix",2040,2055,,-310.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +545,"fix",2055,2070,,-315.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +546,"fix",2070,2085,,-321.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +547,"fix",2085,2100,,-326.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +548,"fix",2100,2115,,-332.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +549,"fix",2115,2130,,-338.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +550,"fix",2130,2145,,-344.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +551,"fix",2145,2160,,-350.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +552,"fix",2160,2175,,-356.25,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +553,"fix",2175,2190,,-362.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +554,"fix",2190,2205,,-368.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +555,"fix",2205,2220,,-374.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +556,"fix",2220,2235,,-380.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +557,"fix",2235,2250,,-386.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +558,"fix",2250,2265,,-392.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +559,"fix",2265,2280,,-398.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +560,"fix",2280,2295,,-404.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +561,"fix",2295,2310,,-410.9,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +562,"fix",2310,2325,,-416.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +563,"fix",2325,2340,,-423.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +564,"fix",2340,2355,,-429.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +565,"fix",2355,2370,,-435.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +566,"fix",2370,2385,,-441.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +567,"fix",2385,2400,,-447.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +568,"fix",2400,2415,,-453.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +569,"fix",2415,2430,,-459.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +570,"fix",2430,2445,,-465.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +571,"fix",2445,2460,,-471.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +572,"fix",2460,2475,,-477.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +573,"fix",2475,2490,,-483.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +574,"fix",2490,2505,,-490.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +575,"fix",2505,2520,,-496.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +576,"fix",2520,2535,,-502.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +577,"fix",2535,2550,,-509.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +578,"fix",2550,2565,,-515.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +579,"fix",2565,2580,,-521.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +580,"fix",2580,2595,,-528,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +581,"fix",2595,2610,,-534.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +582,"fix",2610,2625,,-540.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +583,"fix",2625,2640,,-546.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +584,"fix",2640,2655,,-553.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +585,"fix",2655,2670,,-559.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +586,"fix",2670,2685,,-565.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +587,"fix",2685,2700,,-572.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +588,"fix",2700,2715,,-578.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +589,"fix",2715,2730,,-584.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +590,"fix",2730,2745,,-591.05,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +591,"fix",2745,2760,,-597.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +592,"fix",2760,2775,,-603.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +593,"fix",2775,2790,,-609.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +594,"fix",2790,2805,,-616.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +595,"fix",2805,2820,,-622.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +596,"fix",2820,2835,,-628.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +597,"fix",2835,2850,,-635.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +598,"fix",2850,2865,,-641.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +599,"fix",2865,2880,,-647.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +600,"fix",2880,2895,,-654.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +601,"fix",2895,2910,,-661.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +602,"fix",2910,2925,,-668.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +603,"fix",2925,2940,,-675.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +604,"fix",2940,2955,,-682.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +605,"fix",2955,2970,,-689.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +606,"fix",2970,2985,,-696.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +607,"fix",2985,3000,,-703.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +608,"fix",3000,3015,,-710.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +609,"fix",3015,3030,,-717.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +610,"fix",3030,3045,,-724.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +611,"fix",3045,3060,,-731.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +612,"fix",3060,3075,,-738.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +613,"fix",3075,3090,,-745.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +614,"fix",3090,3105,,-752.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +615,"fix",3105,3120,,-759.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +616,"fix",3120,3135,,-766.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +617,"fix",3135,3150,,-773.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +618,"fix",3150,3165,,-780.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +619,"fix",3165,3180,,-787.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +620,"fix",3180,3195,,-794.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +621,"fix",3195,3210,,-801.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +622,"fix",3210,3225,,-808.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +623,"fix",3225,3240,,-815.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +624,"fix",3240,3255,,-822.6,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +625,"fix",3255,3270,,-829.6,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +626,"fix",3270,3285,,-836.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +627,"fix",3285,3300,,-843.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +628,"fix",3300,3315,,-850.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +629,"fix",3315,3330,,-857.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +630,"fix",3330,3345,,-864.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +631,"fix",3345,3360,,-871.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +632,"fix",3360,3375,,-878.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +633,"fix",3375,3390,,-885.65,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +634,"fix",3390,3405,,-892.65,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +635,"fix",3405,3420,,-899.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +636,"fix",3420,3435,,-906.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +637,"fix",3435,3450,,-913.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +638,"fix",3450,3465,,-920.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +639,"fix",3465,3480,,-927.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +640,"fix",3480,3495,,-934.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +641,"fix",3495,3510,,-941.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +642,"fix",3510,3525,,-948.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +643,"fix",3525,3540,,-955.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +644,"fix",3540,3555,,-962.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +645,"fix",3555,3570,,-969.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +646,"fix",3570,3585,,-976.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +647,"fix",3585,3600,,-983.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +648,"fix",3600,3615,,-990.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +649,"fix",3615,3630,,-997.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +650,"fix",3630,3645,,-1004.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +651,"fix",3645,3660,,-1011.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +652,"fix",3660,3675,,-1018.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +653,"fix",3675,3690,,-1025.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +654,"fix",3690,3705,,-1032.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +655,"fix",3705,3720,,-1039.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +656,"fix",3720,3735,,-1046.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +657,"fix",3735,3750,,-1053.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +658,"fix",3750,3765,,-1060.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +659,"fix",3765,3780,,-1067.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +660,"fix",3780,3795,,-1074.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +661,"fix",3795,3810,,-1081.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +662,"fix",3810,3825,,-1088.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +663,"fix",3825,3840,,-1095.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +664,"fix",3840,3855,,-1102.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +665,"fix",3855,3870,,-1109.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +666,"fix",3870,3885,,-1116.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +667,"fix",3885,3900,,-1123.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +668,"fix",3900,3915,,-1130.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +669,"fix",3915,3930,,-1137.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +670,"fix",3930,3945,,-1144.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +671,"fix",3945,3960,,-1151.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +672,"fix",3960,3975,,-1158.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +673,"fix",3975,3990,,-1165.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +674,"fix",3990,4005,,-1173.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +675,"fix",4005,4020,,-1180.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +676,"fix",4020,4035,,-1188.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +677,"fix",4035,4050,,-1196.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +678,"fix",4050,4065,,-1204.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +679,"fix",4065,4080,,-1212.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +680,"fix",4080,4095,,-1219.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +681,"fix",4095,4110,,-1227.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +682,"fix",4110,4125,,-1235.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +683,"fix",4125,4140,,-1243.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +684,"fix",4140,4155,,-1250.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +685,"fix",4155,4170,,-1258.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +686,"fix",4170,4185,,-1266.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +687,"fix",4185,4200,,-1274.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +688,"fix",4200,4215,,-1282.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +689,"fix",4215,4230,,-1289.9,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +690,"fix",4230,4245,,-1297.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +691,"fix",4245,4260,,-1305.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +692,"fix",4260,4275,,-1313.25,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +693,"fix",4275,4290,,-1321.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +694,"fix",4290,4305,,-1328.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +695,"fix",4305,4320,,-1336.6,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +696,"fix",4320,4335,,-1344.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +697,"fix",4335,4350,,-1352.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +698,"fix",4350,4365,,-1359.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +699,"fix",4365,4380,,-1367.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +700,"fix",4380,4395,,-1375.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +701,"fix",4395,4410,,-1383.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +702,"fix",4410,4425,,-1391.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +703,"fix",4425,4440,,-1398.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +704,"fix",4440,4455,,-1406.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +705,"fix",4455,4470,,-1414.45,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +706,"fix",4470,4485,,-1422.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +707,"fix",4485,4500,,-1430.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +708,"fix",4500,4515,,-1437.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +709,"fix",4515,4530,,-1445.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +710,"fix",4530,4545,,-1453.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +711,"fix",4545,4560,,-1461.15,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +712,"fix",4560,4575,,-1468.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +713,"fix",4575,4590,,-1476.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +714,"fix",4590,4605,,-1484.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +715,"fix",4605,4620,,-1492.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +716,"fix",4620,4635,,-1500.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +717,"fix",4635,4650,,-1507.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +718,"fix",4650,4665,,-1515.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +719,"fix",4665,4680,,-1523.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +720,"fix",4680,4695,,-1531.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +721,"fix",4695,4710,,-1538.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +722,"fix",4710,4725,,-1546.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +723,"fix",4725,4740,,-1554.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +724,"fix",4740,4755,,-1562.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +725,"fix",4755,4770,,-1570.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +726,"fix",4770,4785,,-1577.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +727,"fix",4785,4800,,-1585.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +728,"fix",4800,4815,,-1593.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +729,"fix",4815,4830,,-1601.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +730,"fix",4830,4845,,-1609.05,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +731,"fix",4845,4860,,-1616.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +732,"fix",4860,4875,,-1624.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +733,"fix",4875,4890,,-1632.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +734,"fix",4890,4905,,-1640.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +735,"fix",4905,4920,,-1647.97,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +736,"fix",4920,4935,,-1655.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +737,"fix",4935,4950,,-1663.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +738,"fix",4950,4965,,-1671.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +739,"fix",4965,4980,,-1679.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +740,"fix",4980,4995,,-1686.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +741,"fix",4995,5010,,-1694.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +742,"fix",5010,5025,,-1702.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +743,"fix",5025,5040,,-1711.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +744,"fix",5040,5055,,-1719.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +745,"fix",5055,5070,,-1727.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +746,"fix",5070,5085,,-1735.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +747,"fix",5085,5100,,-1743.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +748,"fix",5100,5115,,-1751.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +749,"fix",5115,5130,,-1759.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +750,"fix",5130,5145,,-1767.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +751,"fix",5145,5160,,-1775.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +752,"fix",5160,5175,,-1783.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +753,"fix",5175,5190,,-1791.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +754,"fix",5190,5205,,-1799.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +755,"fix",5205,5220,,-1807.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +756,"fix",5220,5235,,-1815.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +757,"fix",5235,5250,,-1823.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +758,"fix",5250,5265,,-1831.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +759,"fix",5265,5280,,-1839.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +760,"fix",5280,5295,,-1847.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +761,"fix",5295,5310,,-1855.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +762,"fix",5310,5325,,-1863.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +763,"fix",5325,5340,,-1871.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +764,"fix",5340,5355,,-1879.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +765,"fix",5355,5370,,-1887.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +766,"fix",5370,5385,,-1895.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +767,"fix",5385,5400,,-1903.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +768,"fix",5400,5415,,-1911.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +769,"fix",5415,5430,,-1919.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +770,"fix",5430,5445,,-1927.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +771,"fix",5445,5460,,-1935.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +772,"fix",5460,5475,,-1943.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +773,"fix",5475,5490,,-1951.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +774,"fix",5490,5505,,-1959.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +775,"fix",5505,5520,,-1967.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +776,"fix",5520,5535,,-1975.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +777,"fix",5535,5550,,-1983.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +778,"fix",5550,5565,,-1991.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +779,"fix",5565,5580,,-1999.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +780,"fix",5580,5595,,-2007.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +781,"fix",5595,5610,,-2015.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +782,"fix",5610,5625,,-2023.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +783,"fix",5625,5640,,-2032.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +784,"fix",5640,5655,,-2040.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +785,"fix",5655,5670,,-2048.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +786,"fix",5670,5685,,-2056.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +787,"fix",5685,5700,,-2064.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +788,"fix",5700,5715,,-2072.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +789,"fix",5715,5730,,-2080.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +790,"fix",5730,5745,,-2088.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +791,"fix",5745,5760,,-2096.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +792,"fix",5760,5775,,-2104.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +793,"fix",5775,5790,,-2112.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +794,"fix",5790,5805,,-2120.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +795,"fix",5805,5820,,-2128.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +796,"fix",5820,5835,,-2136.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +797,"fix",5835,5850,,-2144.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +798,"fix",5850,5865,,-2152.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +799,"fix",5865,5880,,-2160.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +800,"fix",5880,5895,,-2168.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +801,"fix",5895,5910,,-2176.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +802,"fix",5910,5925,,-2184.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +803,"fix",5925,5940,,-2192.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +804,"fix",5940,5955,,-2200.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +805,"fix",5955,5970,,-2208.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +806,"fix",5970,5985,,-2216.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +807,"fix",5985,6000,,-2224.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +808,"fix",6000,6015,,-2232.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +809,"fix",6015,6030,,-2240.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +810,"fix",6030,6045,,-2248.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +811,"fix",6045,6060,,-2256.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +812,"fix",6060,6075,,-2264.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +813,"fix",6075,6090,,-2272.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +814,"fix",6090,6105,,-2280.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +815,"fix",6105,6120,,-2288.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +816,"fix",6120,6135,,-2296.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +817,"fix",6135,6150,,-2304.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +818,"fix",6150,6165,,-2312.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +819,"fix",6165,6180,,-2320.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +820,"fix",6180,6195,,-2328.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +821,"fix",6195,6210,,-2336.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +822,"fix",6210,6225,,-2344.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +823,"fix",6225,6240,,-2353.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +824,"fix",6240,6255,,-2361.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +825,"fix",6255,6270,,-2369.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +826,"fix",6270,6285,,-2377.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +827,"fix",6285,6300,,-2385.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +828,"fix",6300,6315,,-2393.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +829,"fix",6315,6330,,-2401.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +830,"fix",6330,6345,,-2409.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +831,"fix",6345,6360,,-2417.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +832,"fix",6360,6375,,-2425.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +833,"fix",6375,6390,,-2433.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +834,"fix",6390,6405,,-2441.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +835,"fix",6405,6420,,-2449.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +836,"fix",6420,6435,,-2457.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +837,"fix",6435,6450,,-2465.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +838,"fix",6450,6465,,-2473.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +839,"fix",6465,6480,,-2481.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +840,"fix",6480,6495,,-2489.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +841,"fix",6495,6510,,-2497.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +842,"fix",6510,6525,,-2505.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +843,"fix",6525,6540,,-2513.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +844,"fix",6540,6555,,-2521.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +845,"fix",6555,6570,,-2529.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +846,"fix",6570,6585,,-2537.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +847,"fix",6585,6600,,-2545.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +848,"fix",6600,6615,,-2553.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +849,"fix",6615,6630,,-2561.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +850,"fix",6630,6645,,-2569.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +851,"fix",6645,6660,,-2577.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +852,"fix",6660,6675,,-2585.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +853,"fix",6675,6690,,-2593.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +854,"fix",6690,6705,,-2601.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +855,"fix",6705,6720,,-2609.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +856,"fix",6720,6735,,-2617.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +857,"fix",6735,6750,,-2625.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +858,"fix",6750,6765,,-2633.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +859,"fix",6765,6780,,-2641.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +860,"fix",6780,6795,,-2649.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +861,"fix",6795,6810,,-2657.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +862,"fix",6810,6825,,-2665.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +863,"fix",6825,6840,,-2674.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +864,"fix",6840,6855,,-2682.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +865,"fix",6855,6870,,-2690.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +866,"fix",6870,6885,,-2698.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +867,"fix",6885,6900,,-2706.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +868,"fix",6900,6915,,-2714.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +869,"fix",6915,6930,,-2722.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +870,"fix",6930,6945,,-2730.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +871,"fix",6945,6960,,-2738.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +872,"fix",6960,6975,,-2746.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +873,"fix",6975,6990,,-2754.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +874,"fix",6990,7005,,-2762.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +875,"fix",7005,7020,,-2770.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +876,"fix",7020,7035,,-2778.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +877,"fix",7035,7050,,-2786.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +878,"fix",7050,7065,,-2794.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +879,"fix",7065,7080,,-2802.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +880,"fix",7080,7095,,-2810.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +881,"fix",7095,7110,,-2818.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +882,"fix",7110,7125,,-2826.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +883,"fix",7125,7140,,-2834.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +884,"fix",7140,7155,,-2842.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +885,"fix",7155,7170,,-2850.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +886,"fix",7170,7185,,-2858.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +887,"fix",7185,7200,,-2866.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +888,"fix",7200,7215,,-2874.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +889,"fix",7215,7230,,-2882.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +890,"fix",7230,7245,,-2890.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +891,"fix",7245,7260,,-2898.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +892,"fix",7260,7275,,-2906.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +893,"fix",7275,7290,,-2914.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +894,"fix",7290,7305,,-2922.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +895,"fix",7305,7320,,-2930.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +896,"fix",7320,7335,,-2938.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +897,"fix",7335,7350,,-2946.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +898,"fix",7350,7365,,-2954.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +899,"fix",7365,7380,,-2962.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +900,"fix",7380,7395,,-2970.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +901,"fix",7395,7410,,-2978.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +902,"fix",7410,7425,,-2986.99,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +903,"fix",7425,7440,,-2995.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +904,"fix",7440,7455,,-3003.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +905,"fix",7455,7470,,-3011.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +906,"fix",7470,7485,,-3019.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +907,"fix",7485,7500,,-3027.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +908,"fix",7500,7500,,-3035.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeII","range","GROSS", +,,,,,,,,,,,,, +909,"fix",0,15,,0,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +910,"fix",15,30,,-2.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +911,"fix",30,45,,-5.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +912,"fix",45,60,,-8.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +913,"fix",60,75,,-11.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +914,"fix",75,90,,-14.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +915,"fix",90,105,,-17.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +916,"fix",105,120,,-20.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +917,"fix",120,135,,-22.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +918,"fix",135,150,,-25.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +919,"fix",150,165,,-28.6,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +920,"fix",165,180,,-31.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +921,"fix",180,195,,-34.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +922,"fix",195,210,,-37.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +923,"fix",210,225,,-40.05,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +924,"fix",225,240,,-42.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +925,"fix",240,255,,-45.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +926,"fix",255,270,,-48.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +927,"fix",270,285,,-51.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +928,"fix",285,300,,-54.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +929,"fix",300,315,,-57.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +930,"fix",315,330,,-60.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +931,"fix",330,345,,-62.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +932,"fix",345,360,,-65.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +933,"fix",360,375,,-68.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +934,"fix",375,390,,-71.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +935,"fix",390,405,,-74.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +936,"fix",405,420,,-77.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +937,"fix",420,435,,-80.1,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +938,"fix",435,450,,-82.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +939,"fix",450,465,,-86.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +940,"fix",465,480,,-89.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +941,"fix",480,495,,-93.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +942,"fix",495,510,,-97.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +943,"fix",510,525,,-100.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +944,"fix",525,540,,-104.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +945,"fix",540,555,,-107.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +946,"fix",555,570,,-111.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +947,"fix",570,585,,-115.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +948,"fix",585,600,,-118.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +949,"fix",600,615,,-122.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +950,"fix",615,630,,-125.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +951,"fix",630,645,,-129.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +952,"fix",645,660,,-133.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +953,"fix",660,675,,-136.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +954,"fix",675,690,,-140.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +955,"fix",690,705,,-144.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +956,"fix",705,720,,-147.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +957,"fix",720,735,,-151.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +958,"fix",735,750,,-154.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +959,"fix",750,765,,-158.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +960,"fix",765,780,,-162.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +961,"fix",780,795,,-165.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +962,"fix",795,810,,-169.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +963,"fix",810,825,,-172.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +964,"fix",825,840,,-176.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +965,"fix",840,855,,-180.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +966,"fix",855,870,,-184.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +967,"fix",870,885,,-188.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +968,"fix",885,900,,-193.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +969,"fix",900,915,,-197.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +970,"fix",915,930,,-202.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +971,"fix",930,945,,-207.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +972,"fix",945,960,,-211.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +973,"fix",960,975,,-216.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +974,"fix",975,990,,-220.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +975,"fix",990,1005,,-225.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +976,"fix",1005,1020,,-229.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +977,"fix",1020,1035,,-234.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +978,"fix",1035,1050,,-239.09,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +979,"fix",1050,1065,,-243.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +980,"fix",1065,1080,,-248.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +981,"fix",1080,1095,,-252.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +982,"fix",1095,1110,,-257.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +983,"fix",1110,1125,,-263.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +984,"fix",1125,1140,,-269.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +985,"fix",1140,1155,,-275.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +986,"fix",1155,1170,,-281.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +987,"fix",1170,1185,,-288.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +988,"fix",1185,1200,,-294.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +989,"fix",1200,1215,,-300.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +990,"fix",1215,1230,,-306.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +991,"fix",1230,1245,,-312.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +992,"fix",1245,1260,,-318.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +993,"fix",1260,1275,,-324.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +994,"fix",1275,1290,,-330.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +995,"fix",1290,1305,,-336.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +996,"fix",1305,1320,,-342.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +997,"fix",1320,1335,,-349.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +998,"fix",1335,1350,,-355.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +999,"fix",1350,1365,,-361.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1000,"fix",1365,1380,,-367.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1001,"fix",1380,1395,,-373.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1002,"fix",1395,1410,,-379.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1003,"fix",1410,1425,,-385.6,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1004,"fix",1425,1440,,-391.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1005,"fix",1440,1455,,-397.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1006,"fix",1455,1470,,-403.9,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1007,"fix",1470,1485,,-410.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1008,"fix",1485,1500,,-416.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1009,"fix",1500,1515,,-422.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1010,"fix",1515,1530,,-428.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1011,"fix",1530,1545,,-435.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1012,"fix",1545,1560,,-442.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1013,"fix",1560,1575,,-449.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1014,"fix",1575,1590,,-456.32,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1015,"fix",1590,1605,,-463.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1016,"fix",1605,1620,,-470.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1017,"fix",1620,1635,,-477.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1018,"fix",1635,1650,,-484.34,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1019,"fix",1650,1665,,-491.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1020,"fix",1665,1680,,-498.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1021,"fix",1680,1695,,-505.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1022,"fix",1695,1710,,-512.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1023,"fix",1710,1725,,-519.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1024,"fix",1725,1740,,-526.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1025,"fix",1740,1755,,-533.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1026,"fix",1755,1770,,-540.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1027,"fix",1770,1785,,-547.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1028,"fix",1785,1800,,-554.4,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1029,"fix",1800,1815,,-561.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1030,"fix",1815,1830,,-568.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1031,"fix",1830,1845,,-575.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1032,"fix",1845,1860,,-582.42,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1033,"fix",1860,1875,,-589.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1034,"fix",1875,1890,,-596.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1035,"fix",1890,1905,,-603.44,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1036,"fix",1905,1920,,-610.45,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1037,"fix",1920,1935,,-617.45,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1038,"fix",1935,1950,,-624.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1039,"fix",1950,1965,,-631.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1040,"fix",1965,1980,,-638.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1041,"fix",1980,1995,,-645.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1042,"fix",1995,2010,,-652.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1043,"fix",2010,2025,,-659.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1044,"fix",2025,2040,,-666.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1045,"fix",2040,2055,,-673.5,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1046,"fix",2055,2070,,-680.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1047,"fix",2070,2085,,-687.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1048,"fix",2085,2100,,-694.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1049,"fix",2100,2115,,-701.52,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1050,"fix",2115,2130,,-708.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1051,"fix",2130,2145,,-715.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1052,"fix",2145,2160,,-722.54,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1053,"fix",2160,2175,,-729.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1054,"fix",2175,2190,,-736.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1055,"fix",2190,2205,,-743.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1056,"fix",2205,2220,,-750.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1057,"fix",2220,2235,,-757.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1058,"fix",2235,2250,,-764.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1059,"fix",2250,2265,,-771.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1060,"fix",2265,2280,,-778.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1061,"fix",2280,2295,,-785.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1062,"fix",2295,2310,,-792.6,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1063,"fix",2310,2325,,-799.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1064,"fix",2325,2340,,-806.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1065,"fix",2340,2355,,-813.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1066,"fix",2355,2370,,-820.62,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1067,"fix",2370,2385,,-827.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1068,"fix",2385,2400,,-834.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1069,"fix",2400,2415,,-841.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1070,"fix",2415,2430,,-848.65,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1071,"fix",2430,2445,,-855.65,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1072,"fix",2445,2460,,-862.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1073,"fix",2460,2475,,-869.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1074,"fix",2475,2490,,-876.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1075,"fix",2490,2505,,-883.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1076,"fix",2505,2520,,-890.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1077,"fix",2520,2535,,-897.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1078,"fix",2535,2550,,-904.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1079,"fix",2550,2565,,-911.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1080,"fix",2565,2580,,-918.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1081,"fix",2580,2595,,-925.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1082,"fix",2595,2610,,-932.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1083,"fix",2610,2625,,-939.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1084,"fix",2625,2640,,-946.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1085,"fix",2640,2655,,-953.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1086,"fix",2655,2670,,-960.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1087,"fix",2670,2685,,-967.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1088,"fix",2685,2700,,-974.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1089,"fix",2700,2715,,-981.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1090,"fix",2715,2730,,-988.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1091,"fix",2730,2745,,-995.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1092,"fix",2745,2760,,-1002.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1093,"fix",2760,2775,,-1009.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1094,"fix",2775,2790,,-1016.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1095,"fix",2790,2805,,-1023.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1096,"fix",2805,2820,,-1030.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1097,"fix",2820,2835,,-1037.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1098,"fix",2835,2850,,-1044.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1099,"fix",2850,2865,,-1051.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1100,"fix",2865,2880,,-1058.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1101,"fix",2880,2895,,-1065.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1102,"fix",2895,2910,,-1072.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1103,"fix",2910,2925,,-1079.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1104,"fix",2925,2940,,-1086.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1105,"fix",2940,2955,,-1093.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1106,"fix",2955,2970,,-1100.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1107,"fix",2970,2985,,-1107.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1108,"fix",2985,3000,,-1114.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1109,"fix",3000,3015,,-1121.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1110,"fix",3015,3030,,-1128.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1111,"fix",3030,3045,,-1135.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1112,"fix",3045,3060,,-1142.89,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1113,"fix",3060,3075,,-1149.9,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1114,"fix",3075,3090,,-1156.9,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1115,"fix",3090,3105,,-1163.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1116,"fix",3105,3120,,-1170.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1117,"fix",3120,3135,,-1177.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1118,"fix",3135,3150,,-1184.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1119,"fix",3150,3165,,-1191.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1120,"fix",3165,3180,,-1198.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1121,"fix",3180,3195,,-1206.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1122,"fix",3195,3210,,-1214.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1123,"fix",3210,3225,,-1221.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1124,"fix",3225,3240,,-1229.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1125,"fix",3240,3255,,-1237.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1126,"fix",3255,3270,,-1245.3,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1127,"fix",3270,3285,,-1253.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1128,"fix",3285,3300,,-1260.87,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1129,"fix",3300,3315,,-1268.65,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1130,"fix",3315,3330,,-1276.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1131,"fix",3330,3345,,-1284.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1132,"fix",3345,3360,,-1292,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1133,"fix",3360,3375,,-1299.79,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1134,"fix",3375,3390,,-1307.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1135,"fix",3390,3405,,-1315.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1136,"fix",3405,3420,,-1323.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1137,"fix",3420,3435,,-1330.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1138,"fix",3435,3450,,-1338.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1139,"fix",3450,3465,,-1346.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1140,"fix",3465,3480,,-1354.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1141,"fix",3480,3495,,-1362.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1142,"fix",3495,3510,,-1369.85,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1143,"fix",3510,3525,,-1377.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1144,"fix",3525,3540,,-1385.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1145,"fix",3540,3555,,-1393.2,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1146,"fix",3555,3570,,-1400.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1147,"fix",3570,3585,,-1408.77,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1148,"fix",3585,3600,,-1416.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1149,"fix",3600,3615,,-1424.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1150,"fix",3615,3630,,-1432.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1151,"fix",3630,3645,,-1439.9,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1152,"fix",3645,3660,,-1447.69,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1153,"fix",3660,3675,,-1455.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1154,"fix",3675,3690,,-1463.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1155,"fix",3690,3705,,-1471.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1156,"fix",3705,3720,,-1478.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1157,"fix",3720,3735,,-1486.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1158,"fix",3735,3750,,-1494.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1159,"fix",3750,3765,,-1502.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1160,"fix",3765,3780,,-1509.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1161,"fix",3780,3795,,-1517.75,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1162,"fix",3795,3810,,-1525.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1163,"fix",3810,3825,,-1533.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1164,"fix",3825,3840,,-1541.1,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1165,"fix",3840,3855,,-1548.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1166,"fix",3855,3870,,-1556.67,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1167,"fix",3870,3885,,-1564.45,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1168,"fix",3885,3900,,-1572.24,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1169,"fix",3900,3915,,-1580.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1170,"fix",3915,3930,,-1587.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1171,"fix",3930,3945,,-1595.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1172,"fix",3945,3960,,-1603.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1173,"fix",3960,3975,,-1611.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1174,"fix",3975,3990,,-1618.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1175,"fix",3990,4005,,-1626.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1176,"fix",4005,4020,,-1634.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1177,"fix",4020,4035,,-1642.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1178,"fix",4035,4050,,-1650.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1179,"fix",4050,4065,,-1657.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1180,"fix",4065,4080,,-1665.65,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1181,"fix",4080,4095,,-1673.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1182,"fix",4095,4110,,-1681.22,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1183,"fix",4110,4125,,-1689,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1184,"fix",4125,4140,,-1696.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1185,"fix",4140,4155,,-1704.57,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1186,"fix",4155,4170,,-1712.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1187,"fix",4170,4185,,-1720.14,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1188,"fix",4185,4200,,-1727.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1189,"fix",4200,4215,,-1735.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1190,"fix",4215,4230,,-1743.49,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1191,"fix",4230,4245,,-1751.27,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1192,"fix",4245,4260,,-1759.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1193,"fix",4260,4275,,-1766.84,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1194,"fix",4275,4290,,-1774.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1195,"fix",4290,4305,,-1782.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1196,"fix",4305,4320,,-1790.19,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1197,"fix",4320,4335,,-1797.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1198,"fix",4335,4350,,-1805.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1199,"fix",4350,4365,,-1813.55,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1200,"fix",4365,4380,,-1821.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1201,"fix",4380,4395,,-1829.12,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1202,"fix",4395,4410,,-1836.9,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1203,"fix",4410,4425,,-1844.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1204,"fix",4425,4440,,-1852.47,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1205,"fix",4440,4455,,-1860.25,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1206,"fix",4455,4470,,-1868.04,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1207,"fix",4470,4485,,-1875.82,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1208,"fix",4485,4500,,-1883.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1209,"fix",4500,4515,,-1891.39,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1210,"fix",4515,4530,,-1899.17,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1211,"fix",4530,4545,,-1906.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1212,"fix",4545,4560,,-1914.74,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1213,"fix",4560,4575,,-1922.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1214,"fix",4575,4590,,-1930.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1215,"fix",4590,4605,,-1938.1,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1216,"fix",4605,4620,,-1945.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1217,"fix",4620,4635,,-1953.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1218,"fix",4635,4650,,-1961.45,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1219,"fix",4650,4665,,-1969.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1220,"fix",4665,4680,,-1977.02,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1221,"fix",4680,4695,,-1984.8,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1222,"fix",4695,4710,,-1992.59,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1223,"fix",4710,4725,,-2000.37,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1224,"fix",4725,4740,,-2008.15,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1225,"fix",4740,4755,,-2015.94,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1226,"fix",4755,4770,,-2023.72,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1227,"fix",4770,4785,,-2031.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1228,"fix",4785,4800,,-2039.29,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1229,"fix",4800,4815,,-2047.07,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1230,"fix",4815,4830,,-2054.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1231,"fix",4830,4845,,-2062.64,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1232,"fix",4845,4860,,-2070.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1233,"fix",4860,4875,,-2078.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1234,"fix",4875,4890,,-2086,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1235,"fix",4890,4905,,-2093.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1236,"fix",4905,4920,,-2101.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1237,"fix",4920,4935,,-2109.35,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1238,"fix",4935,4950,,-2117.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1239,"fix",4950,4965,,-2124.92,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1240,"fix",4965,4980,,-2132.7,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1241,"fix",4980,4995,,-2140.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1242,"fix",4995,5010,,-2148.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1243,"fix",5010,5025,,-2156.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1244,"fix",5025,5040,,-2164.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1245,"fix",5040,5055,,-2172.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1246,"fix",5055,5070,,-2180.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1247,"fix",5070,5085,,-2188.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1248,"fix",5085,5100,,-2196.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1249,"fix",5100,5115,,-2204.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1250,"fix",5115,5130,,-2212.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1251,"fix",5130,5145,,-2220.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1252,"fix",5145,5160,,-2228.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1253,"fix",5160,5175,,-2236.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1254,"fix",5175,5190,,-2244.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1255,"fix",5190,5205,,-2252.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1256,"fix",5205,5220,,-2260.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1257,"fix",5220,5235,,-2268.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1258,"fix",5235,5250,,-2276.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1259,"fix",5250,5265,,-2284.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1260,"fix",5265,5280,,-2293.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1261,"fix",5280,5295,,-2301.03,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1262,"fix",5295,5310,,-2309.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1263,"fix",5310,5325,,-2317.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1264,"fix",5325,5340,,-2325.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1265,"fix",5340,5355,,-2333.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1266,"fix",5355,5370,,-2341.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1267,"fix",5370,5385,,-2349.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1268,"fix",5385,5400,,-2357.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1269,"fix",5400,5415,,-2365.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1270,"fix",5415,5430,,-2373.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1271,"fix",5430,5445,,-2381.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1272,"fix",5445,5460,,-2389.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1273,"fix",5460,5475,,-2397.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1274,"fix",5475,5490,,-2405.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1275,"fix",5490,5505,,-2413.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1276,"fix",5505,5520,,-2421.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1277,"fix",5520,5535,,-2429.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1278,"fix",5535,5550,,-2437.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1279,"fix",5550,5565,,-2445.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1280,"fix",5565,5580,,-2453.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1281,"fix",5580,5595,,-2461.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1282,"fix",5595,5610,,-2469.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1283,"fix",5610,5625,,-2477.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1284,"fix",5625,5640,,-2485.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1285,"fix",5640,5655,,-2493.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1286,"fix",5655,5670,,-2501.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1287,"fix",5670,5685,,-2509.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1288,"fix",5685,5700,,-2517.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1289,"fix",5700,5715,,-2525.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1290,"fix",5715,5730,,-2533.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1291,"fix",5730,5745,,-2541.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1292,"fix",5745,5760,,-2549.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1293,"fix",5760,5775,,-2557.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1294,"fix",5775,5790,,-2565.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1295,"fix",5790,5805,,-2573.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1296,"fix",5805,5820,,-2581.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1297,"fix",5820,5835,,-2589.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1298,"fix",5835,5850,,-2597.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1299,"fix",5850,5865,,-2605.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1300,"fix",5865,5880,,-2614.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1301,"fix",5880,5895,,-2622.03,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1302,"fix",5895,5910,,-2630.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1303,"fix",5910,5925,,-2638.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1304,"fix",5925,5940,,-2646.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1305,"fix",5940,5955,,-2654.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1306,"fix",5955,5970,,-2662.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1307,"fix",5970,5985,,-2670.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1308,"fix",5985,6000,,-2678.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1309,"fix",6000,6015,,-2686.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1310,"fix",6015,6030,,-2694.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1311,"fix",6030,6045,,-2702.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1312,"fix",6045,6060,,-2710.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1313,"fix",6060,6075,,-2718.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1314,"fix",6075,6090,,-2726.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1315,"fix",6090,6105,,-2734.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1316,"fix",6105,6120,,-2742.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1317,"fix",6120,6135,,-2750.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1318,"fix",6135,6150,,-2758.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1319,"fix",6150,6165,,-2766.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1320,"fix",6165,6180,,-2774.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1321,"fix",6180,6195,,-2782.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1322,"fix",6195,6210,,-2790.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1323,"fix",6210,6225,,-2798.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1324,"fix",6225,6240,,-2806.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1325,"fix",6240,6255,,-2814.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1326,"fix",6255,6270,,-2822.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1327,"fix",6270,6285,,-2830.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1328,"fix",6285,6300,,-2838.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1329,"fix",6300,6315,,-2846.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1330,"fix",6315,6330,,-2854.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1331,"fix",6330,6345,,-2862.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1332,"fix",6345,6360,,-2870.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1333,"fix",6360,6375,,-2878.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1334,"fix",6375,6390,,-2886.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1335,"fix",6390,6405,,-2894.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1336,"fix",6405,6420,,-2902.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1337,"fix",6420,6435,,-2910.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1338,"fix",6435,6450,,-2918.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1339,"fix",6450,6465,,-2926.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1340,"fix",6465,6480,,-2935.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1341,"fix",6480,6495,,-2943.03,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1342,"fix",6495,6510,,-2951.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1343,"fix",6510,6525,,-2959.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1344,"fix",6525,6540,,-2967.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1345,"fix",6540,6555,,-2975.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1346,"fix",6555,6570,,-2983.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1347,"fix",6570,6585,,-2991.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1348,"fix",6585,6600,,-2999.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1349,"fix",6600,6615,,-3007.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1350,"fix",6615,6630,,-3015.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1351,"fix",6630,6645,,-3023.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1352,"fix",6645,6660,,-3031.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1353,"fix",6660,6675,,-3039.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1354,"fix",6675,6690,,-3047.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1355,"fix",6690,6705,,-3055.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1356,"fix",6705,6720,,-3063.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1357,"fix",6720,6735,,-3071.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1358,"fix",6735,6750,,-3079.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1359,"fix",6750,6765,,-3087.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1360,"fix",6765,6780,,-3095.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1361,"fix",6780,6795,,-3103.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1362,"fix",6795,6810,,-3111.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1363,"fix",6810,6825,,-3119.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1364,"fix",6825,6840,,-3127.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1365,"fix",6840,6855,,-3135.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1366,"fix",6855,6870,,-3143.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1367,"fix",6870,6885,,-3151.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1368,"fix",6885,6900,,-3159.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1369,"fix",6900,6915,,-3167.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1370,"fix",6915,6930,,-3175.76,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1371,"fix",6930,6945,,-3183.78,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1372,"fix",6945,6960,,-3191.81,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1373,"fix",6960,6975,,-3199.83,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1374,"fix",6975,6990,,-3207.86,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1375,"fix",6990,7005,,-3215.88,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1376,"fix",7005,7020,,-3223.91,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1377,"fix",7020,7035,,-3231.93,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1378,"fix",7035,7050,,-3239.96,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1379,"fix",7050,7065,,-3247.98,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1380,"fix",7065,7080,,-3256.01,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1381,"fix",7080,7095,,-3264.03,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1382,"fix",7095,7110,,-3272.06,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1383,"fix",7110,7125,,-3280.08,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1384,"fix",7125,7140,,-3288.11,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1385,"fix",7140,7155,,-3296.13,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1386,"fix",7155,7170,,-3304.16,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1387,"fix",7170,7185,,-3312.18,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1388,"fix",7185,7200,,-3320.21,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1389,"fix",7200,7215,,-3328.23,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1390,"fix",7215,7230,,-3336.26,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1391,"fix",7230,7245,,-3344.28,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1392,"fix",7245,7260,,-3352.31,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1393,"fix",7260,7275,,-3360.33,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1394,"fix",7275,7290,,-3368.36,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1395,"fix",7290,7305,,-3376.38,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1396,"fix",7305,7320,,-3384.41,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1397,"fix",7320,7335,,-3392.43,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1398,"fix",7335,7350,,-3400.46,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1399,"fix",7350,7365,,-3408.48,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1400,"fix",7365,7380,,-3416.51,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1401,"fix",7380,7395,,-3424.53,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1402,"fix",7395,7410,,-3432.56,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1403,"fix",7410,7425,,-3440.58,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1404,"fix",7425,7440,,-3448.61,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1405,"fix",7440,7455,,-3456.63,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1406,"fix",7455,7470,,-3464.66,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1407,"fix",7470,7485,,-3472.68,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1408,"fix",7485,7500,,-3480.71,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +1409,"fix",7500,7500,,-3488.73,"Precompte Professionnel","Precompte Professionnel Bareme 2011",130,"P.P","hr_payroll_rules_baremeIII","range","GROSS", +,,,,,,,,,,,,, +1410,"fix",1,1,,31,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1411,"fix",2,2,,85,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1412,"fix",3,3,,229,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1413,"fix",4,4,,418,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1414,"fix",5,5,,618,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1415,"fix",6,6,,817,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1416,"fix",7,7,,1016,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1417,"fix",8,8,,1231,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1418,"fix",9,9,,1452,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +1419,"fix",10,10,,1673,"Child Allowance Belgium","Child Allowance Belgium",142,"Ch.A","hr_payroll_rules_child","range","employee.children", +,,,,,,,,,,,,, +1420,"fix",1,1,,85,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1421,"fix",2,2,,418,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1422,"fix",3,3,,817,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1423,"fix",4,4,,1231,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1424,"fix",5,5,,1673,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1425,"fix",6,6,,2115,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1426,"fix",7,7,,2557,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1427,"fix",8,8,,2999,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1428,"fix",9,9,,3441,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", +1429,"fix",10,10,,3883,"Child Allowance for the Disabled Belgium","Child Allowance Belgium",144,"Ch.Handicap","hr_payroll_rules_child_handicap","range","employee.number_handicap", diff --git a/addons/hr_payroll_l10n_be/hr_payroll_l10n_be.py b/addons/hr_payroll_l10n_be/hr_payroll_l10n_be.py new file mode 100644 index 0000000000000000000000000000000000000000..3c98e99cd718dc402030f512e5031e2746ea64c1 --- /dev/null +++ b/addons/hr_payroll_l10n_be/hr_payroll_l10n_be.py @@ -0,0 +1,63 @@ +#-*- coding:utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved +# d$ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import time +from datetime import date +from datetime import datetime +from datetime import timedelta + +import netsvc +from osv import fields, osv +import tools +from tools.translate import _ +import decimal_precision as dp + + + +class hr_contract_be(osv.osv): + _inherit = 'hr.contract' + + _columns = { + 'reim_travel':fields.float('Reimbursement of travel expenses', digits=(16,2)), + 'company_car_emp':fields.float('Company car employer', digits=(16,2)), + 'company_car_wkr':fields.float('Company Car Deduction for Worker', digits=(16,2)), + 'mis_ex_onss':fields.float('Miscellaneous exempt ONSS ', digits=(16,2)), + 'ch_value':fields.float('Check Value Meal ', digits=(16,2)), + 'ch_worker':fields.float('Check Value Meal - by worker ', digits=(16,2)), + 'insurance':fields.float('Insurance Group - by worker ', digits=(16,2)), + 'advantage':fields.float('Benefits of various nature ', digits=(16,2)), + 'suppl_net':fields.float('Net supplements', digits=(16,2)), + 'retained_net':fields.float('Net retained ', digits=(16,2)), + } +hr_contract_be() + +class hr_employee_be(osv.osv): + _inherit = 'hr.employee' + + _columns = { + 'statut_fiscal':fields.selection([('without income','Without Income'),('with income','With Income')], 'Tax status for spouse'), + 'handicap':fields.boolean('Disabled Spouse', help="if recipient spouse is declared disabled by law"), + 'handicap_child':fields.boolean('Disabled Children', help="if recipient children is/are declared disabled by law"), + 'resident':fields.boolean('Nonresident', help="if recipient lives in a foreign country"), + 'number_handicap':fields.integer('Number of disabled children'), + } +hr_employee_be() diff --git a/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_data.xml b/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_data.xml new file mode 100644 index 0000000000000000000000000000000000000000..c57e0a9118e90e1e1ab3bf51dac8795e35859842 --- /dev/null +++ b/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_data.xml @@ -0,0 +1,316 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record id="hr_payroll_head_salary" model="hr.salary.rule.category"> + <field name="name">Brut Total</field> + <field name="code">BRUT</field> + </record> + <record id="hr_payroll.COMP" model="hr.salary.rule.category"> + <field name="name">Company Part</field> + <field name="code">COMP. PART</field> + </record> + <record id="hr_payroll_head_onss" model="hr.salary.rule.category"> + <field name="name">Cotisation Sociale Travailleur</field> + <field name="code">ONSS</field> + <field name="parent_id" ref="hr_payroll.ALW"/> + </record> + <record id="hr_payroll_head_div_impos" model="hr.salary.rule.category"> + <field name="name">Total des divers imposables</field> + <field name="code">DIV. IMPOS</field> + <field name="parent_id" ref="hr_payroll.ALW"/> + </record> + <record id="hr_payroll_head_pp" model="hr.salary.rule.category"> + <field name="name">Precompte Professionnel Bareme 2011</field> + <field name="code">P.P.</field> + <field name="parent_id" ref="hr_payroll.DED"/> + </record> + <record id="hr_payroll_head_div_net" model="hr.salary.rule.category"> + <field name="name">Total divers net</field> + <field name="code">DIV. NET</field> + <field name="parent_id" ref="hr_payroll.DED"/> + </record> + <record id="hr_payroll_head_child_alw" model="hr.salary.rule.category"> + <field name="name">Child Allowance Belgium</field> + <field name="code">Ch.A</field> + <field name="parent_id" ref="hr_payroll.DED"/> + </record> + +<!-- HR SALAR RULES--> + <record id="hr_payroll_rules_maj" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_salary"/> + <field name="name">Premium pay 108% Worker</field> + <field name="code">SALARY</field> + <field name="condition_select">none</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = contract.wage * 1.08</field> + </record> + <record id="hr_payroll_rules_employee" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_salary"/> + <field name="name">Salary Employee</field> + <field name="code">SALARY</field> + <field name="condition_select">none</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = contract.wage * 1</field> + <field name="appears_on_payslip" eval="False"/> + </record> + <record id="hr_payroll_rules_onss_rule" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_onss"/> + <field name="name">Office National de Sécurité Sociale</field> + <field name="code">ONSS</field> + <field name="sequence">41</field> + <field name="condition_select">none</field> + <field name="amount_select">percentage</field> + <field name="amount_percentage_base">SALARY</field> + <field name="amount_percentage">-13.07</field> + </record> + <record id="hr_payroll_rules_p_p_b1" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_pp"/> + <field name="name">Precompte Professionnel Bareme 2011</field> + <field name="code">P.P</field> + <field name="sequence">120</field> + <field name="amount_select">fix</field> + <field name="condition_select">none</field> + <field name="appears_on_payslip" eval="False"/> + </record> + <record id="hr_payroll_rules_bareme" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_pp"/> + <field name="parent_rule_id" ref="hr_payroll_rules_p_p_b1"/> + <field name="name">Precompte Professionnel</field> + <field name="code">B.I.</field> + <field name="amount_select">fix</field> + <field name="sequence">120</field> + <field name="condition_select">python</field> + <field name="appears_on_payslip" eval="False"/> + <field name="condition_python">result = (((employee.marital=='single') or (employee.marital=='married' and employee.statut_fiscal=='with income')) and (employee.resident!=True))</field> + </record> + <record id="hr_payroll_rules_baremeII" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_pp"/> + <field name="parent_rule_id" ref="hr_payroll_rules_p_p_b1"/> + <field name="name">Precompte Professionnel</field> + <field name="code">B.II</field> + <field name="amount_select">fix</field> + <field name="sequence">120</field> + <field name="condition_select">python</field> + <field name="appears_on_payslip" eval="False"/> + <field name="condition_python">result = ((employee.marital=='married' and employee.statut_fiscal=='without income') and (employee.resident!=True))</field> + </record> + <record id="hr_payroll_rules_baremeIII" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_pp"/> + <field name="parent_rule_id" ref="hr_payroll_rules_p_p_b1"/> + <field name="name">Precompte Professionnel</field> + <field name="code">B.III</field> + <field name="amount_select">fix</field> + <field name="sequence">120</field> + <field name="condition_select">python</field> + <field name="condition_python">result = employee.resident==True</field> + <field name="appears_on_payslip" eval="False"/> + </record> + <record id="hr_payroll_rules_child_alw" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="name">Child Allowance Belgium</field> + <field name="code">Child.Allowance</field> + <field name="sequence">140</field> + <field name="amount_select">fix</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(employee.children)</field> + </record> + <record id="hr_payroll_rules_child" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="parent_rule_id" ref="hr_payroll_rules_child_alw"/> + <field name="name">Child Allowance Belgium</field> + <field name="code">Ch.A</field> + <field name="amount_select">fix</field> + <field name="sequence">141</field> + <field name="note">The recipient of income is an isolated or spouse beneficiary's income has also + earned income (Schedule I / II scale) + + A. When the gross monthly earnings do not exceed € 7,500, the withholding tax is determined according to the Schedule I. + The following reductions are then deducted from the payroll tax determined in accordance with this schedule: + + a) Reduction for dependent children</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(employee.children)</field> + </record> + <record id="hr_payroll_rules_child_handicap" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="parent_rule_id" ref="hr_payroll_rules_child_alw"/> + <field name="name">Child Allowance for the Disabled Belgium</field> + <field name="code">Ch.Handicap</field> + <field name="amount_select">fix</field> + <field name="sequence">143</field> + <field name="note">"Disabled child" means: + + the child reaches at least 66% of failure or reduced physical or mental capacity + the head of one or more conditions; + + + the child that is established, regardless of age, that due to developments and recognized + before age 65</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(employee.number_handicap)</field> + </record> + <record id="hr_payroll_rules_spouse_handicap" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_div_net"/> + <field name="name">Disabled Spouse Allowance</field> + <field name="code">Sp.handicap</field> + <field name="amount_select">fix</field> + <field name="sequence">145</field> + <field name="amount_fix">31</field> + <field name="condition_select">python</field> + <field name="condition_python">result = employee.handicap==True</field> + </record> + <record id="hr_payroll_rules_company_car_parent" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_div_impos"/> + <field name="name">Company Car</field> + <field name="code">car</field> + <field name="amount_select">fix</field> + <field name="sequence">15</field> + <field name="condtion_select">none</field> + <field name="appears_on_payslip" eval="False"/> + </record> + <record id="hr_payroll_rules_parent_company_car" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_div_impos"/> + <field name="name">Company car</field> + <field name="parent_rule_id" ref="hr_payroll_rules_company_car_parent"/> + <field name="code">CAR 1</field> + <field name="sequence">15</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.company_car_wkr)</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = contract.company_car_wkr</field> + </record> + <record id="hr_payroll_rules_company_car_2" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_div_net"/> + <field name="name">Retenue Company car</field> + <field name="parent_rule_id" ref="hr_payroll_rules_company_car_parent"/> + <field name="code">CAR 2</field> + <field name="sequence">160</field> + <field name="condition_select">python</field> + <field name="condition_python">result = contract.company_car_wkr</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = -contract.company_car_wkr</field> + </record> + <record id="hr_payroll_rules_company_car_emp" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.COMP"/> + <field name="name">Company car, Company part</field> + <field name="parent_rule_id" ref="hr_payroll_rules_company_car_parent"/> + <field name="code">CAR 3</field> + <field name="sequence">160</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.company_car_emp)</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = -contract.company_car_emp</field> + <field name="appears_on_payslip" eval="False"/> + </record> + <record id="hr_payroll_rules_parent_ch" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_div_net"/> + <field name="name">Check Value Meal</field> + <field name="code">Ch.M.</field> + <field name="amount_select">fix</field> + <field name="sequence">165</field> + <field name="condition_select">none</field> + <field name="appears_on_payslip" eval="False"/> + </record> + <record id="hr_payroll_rules_ch_value" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.COMP"/> + <field name="parent_rule_id" ref="hr_payroll_rules_parent_ch"/> + <field name="name">Check Value Meal, Company Part</field> + <field name="code">Ch.M.</field> + <field name="amount_select">fix</field> + <field name="sequence">165</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = -(contract.ch_value - contract.ch_worker) * worked_days.WORK100.number_of_days</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.ch_value)</field> + <field name="appears_on_payslip" eval="False"/> + </record> + <record id="hr_payroll_rules_ch_worker" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_div_net"/> + <field name="name">Retain on Check Value Meal</field> + <field name="parent_rule_id" ref="hr_payroll_rules_parent_ch"/> + <field name="code">Meal Check</field> + <field name="sequence">165</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.ch_worker)</field> + <field name="amount_select">percentage</field> + <field name="amount_percentage">-100.0</field> + <field name="amount_percentage_base">contract.ch_worker</field> + <field name="quantity">worked_days.WORK100.number_of_days</field> + <field name="appears_on_payslip" eval="True"/> + </record> + <record id="hr_payroll_rules_reim_travel" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="name">Reimbursement of travel expenses</field> + <field name="code">Tr.E</field> + <field name="amount_select">code</field> + <field name="sequence">65</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.reim_travel)</field> + <field name="amount_python_compute">result = contract.reim_travel</field> + </record> + <record id="hr_payroll_rules_mis_ex_onss" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="name">Miscellaneous exempt ONSS</field> + <field name="code">M.ONSS</field> + <field name="amount_select">code</field> + <field name="sequence">165</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.mis_ex_onss)</field> + <field name="amount_python_compute">result = -contract.mis_ex_onss</field> + </record> + <record id="hr_payroll_rules_insurance" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="name">Insurance</field> + <field name="code">Ins</field> + <field name="amount_select">code</field> + <field name="sequence">165</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.insurance)</field> + <field name="amount_pyton_compute">result = -contract.insurance</field> + </record> + <record id="hr_payroll_rules_advantage" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="name">Benefits of various nature</field> + <field name="code">Ben</field> + <field name="amount_select">code</field> + <field name="sequence">10</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.advantage)</field> + <field name="amount_python_compute">result = contract.advantage</field> + </record> + <record id="hr_payroll_rules_suppl_net" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_div_net"/> + <field name="name">Net Supplement</field> + <field name="code">Net.S</field> + <field name="amount_select">code</field> + <field name="sequence">185</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.suppl_net)</field> + <field name="amount_python_compute">result = contract.suppl_net</field> + </record> + <record id="hr_payroll_rules_retained_net" model="hr.salary.rule"> + <field name="category_id" ref="hr_payroll_head_div_net"/> + <field name="name">Net retained</field> + <field name="code">Net.R</field> + <field name="amount_select">code</field> + <field name="sequence">190</field> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.retained_net)</field> + <field name="amount_python_compute">result = -contract.retained_net</field> + </record> + <record id="hr_payroll_salary_structure_employee" model="hr.payroll.structure"> + <field name="code">C.P.218</field> + <field name="name">Belgian Employee</field> + <field eval="[(6, 0, [ref('hr_payroll_rules_company_car_parent'),ref('hr_payroll_rules_onss_rule'),ref('hr_payroll_rules_employee'),ref('hr_payroll_rules_p_p_b1'),ref('hr_payroll_rules_child_alw'),ref('hr_payroll_rules_parent_ch'),ref('hr_payroll_rules_reim_travel'),ref('hr_payroll_rules_mis_ex_onss'),ref('hr_payroll_rules_insurance'),ref('hr_payroll_rules_advantage'),ref('hr_payroll_rules_suppl_net'),ref('hr_payroll_rules_retained_net')])]" name="rule_ids"/> + <field name="company_id" ref="base.main_company"/> + <field name="parent_id" ref="hr_payroll.structure_base"/> + </record> + <record id="hr_payroll_salary_structure_worker" model="hr.payroll.structure"> + <field name="code">C.P.219</field> + <field name="name">Belgian Worker</field> + <field eval="[(6, 0, [ref('hr_payroll_rules_maj'), ref('hr_payroll_rules_onss_rule'),ref('hr_payroll_rules_p_p_b1'),ref('hr_payroll_rules_child_alw'),ref('hr_payroll_rules_parent_ch'),ref('hr_payroll_rules_reim_travel'),ref('hr_payroll_rules_mis_ex_onss'),ref('hr_payroll_rules_insurance'),ref('hr_payroll_rules_advantage'),ref('hr_payroll_rules_suppl_net'),ref('hr_payroll_rules_retained_net')])]" name="rule_ids"/> + <field name="company_id" ref="base.main_company"/> + <field name="parent_id" ref="hr_payroll.structure_base"/> + </record> + </data> +</openerp> diff --git a/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_demo.xml b/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_demo.xml new file mode 100644 index 0000000000000000000000000000000000000000..c9e284f8e9bbb3e069af63ccdfe0d6007f15f10e --- /dev/null +++ b/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_demo.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + + <!-- Employee Contract --> + <record id="hr_payroll.hr_contract_firstcontract1" model="hr.contract"> + <field name="name">Bonamy's Contract (Belgium payroll C.P. 218)</field> + <field name="struct_id" ref="hr_payroll_salary_structure_employee"/> + <field eval="2500.0" name="wage"/> + <field eval="6.0" name="ch_value"/> + <field eval="1.09" name="ch_worker"/> + <field eval="250.0" name="company_car_emp"/> + <field eval="110" name="company_car_wkr"/> + <field eval="50.0" name="suppl_net"/> + <field name="working_hours" ref="resource.timesheet_group1"/> + </record> + + <record id="hr_payroll.hr_employee_bonamy0" model="hr.employee"> + <field name="marital">single</field> + </record> + + </data> +</openerp> + diff --git a/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_view.xml b/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..c4dca86166d4bdf2514887e7e87583b18685e4ae --- /dev/null +++ b/addons/hr_payroll_l10n_be/hr_payroll_l10n_be_view.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record id="hr_contract_form_inherit" model="ir.ui.view"> + <field name="name">hr.contract.form.inherit</field> + <field name="model">hr.contract</field> + <field name="type">form</field> + <field name="priority">20</field> + <field name="inherit_id" ref="hr_contract.hr_contract_view_form"/> + <field name="arch" type="xml"> + <data> + <xpath expr="/form/notebook/page[@name='information']/group[@name='right_column']/field[@name='struct_id']" position="after"> + <field name="reim_travel"/> + <field name="company_car_emp"/> + <field name="mis_ex_onss"/> + <field name="ch_value"/> + <field name="advantage"/> + <separator string="Miscellaneous" colspan="2"/> + <field name="suppl_net"/> + <field name="retained_net"/> + </xpath> + <xpath expr="/form/notebook/page/group/field[@name='trial_date_end']" position="after"> + <group colspan="2" col="2"> + <separator string="by Worker" colspan="2"/> + <field name="insurance"/> + <field name="ch_worker"/> + <field name="company_car_wkr"/> + </group> + </xpath> + </data> + </field> + </record> + + <record id="view_employee_marital_required_form" model="ir.ui.view"> + <field name="name">hr.employee.employee.inherit.form</field> + <field name="model">hr.employee</field> + <field name="type">form</field> + <field name="priority">25</field> + <field name="inherit_id" ref="hr.view_employee_form"/> + <field name="arch" type="xml"> + <field name="marital" position="replace"> + </field> + </field> + </record> + + <record id="hr_contract_hr_hr_employee_view_for" model="ir.ui.view"> + <field name="name">hr.hr.employee.view.for</field> + <field name="model">hr.employee</field> + <field name="inherit_id" ref="hr.view_employee_form"/> + <field name="priority">30</field> + <field name="arch" type="xml"> + <data> + <xpath expr="/form/notebook/page/group/field[@name='birthday']" position="after"> + <field name="marital" required="True"/> + </xpath> + <xpath expr="/form/notebook/page/group/field[@name='gender']" position="before"> + <field name="resident" eval="False"/> + </xpath> + <xpath expr="/form/notebook/page/group/field[@name='marital']" position="after"> + <field name="statut_fiscal" attrs="{'invisible':[('marital','!=','married')],'required':[('marital','=','married')]}" colspan="1" help="if spouse has professionnel income or not"/> + <field name="handicap" attrs="{'invisible':[('marital','!=','married')]}" colspan="1"/> + </xpath> + <xpath expr="/form/notebook/page/group/field[@name='children']" position="after"> + <field name="handicap_child"/> + <field name="number_handicap" attrs="{'invisible':[('handicap_child','!=',True)]}" colspan="1" help="number of dependent children declared as disabled"/> + </xpath> + </data> + </field> + </record> + </data> +</openerp> diff --git a/addons/hr_payroll_l10n_be/security/hr.salary.rule.csv b/addons/hr_payroll_l10n_be/security/hr.salary.rule.csv new file mode 100644 index 0000000000000000000000000000000000000000..a8b6906d244048ab2c950a3ca06fff6d4a7741a7 --- /dev/null +++ b/addons/hr_payroll_l10n_be/security/hr.salary.rule.csv @@ -0,0 +1,3 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_hr_salary_rule_category","hr.salary.rule.category","model_hr_salary_rule_category","base.group_hr_user",1,1,1,1 +"access_hr_salary_rule","hr.salary.rule","model_hr_salary_rule","base.group_hr_user",1,1,1,1 diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 3456f9c8bbe540da6fec42d7add439bcd768a67e..7fde90dc8e96e6121cedf9ec1f00ebefb3d3490a 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -29,6 +29,9 @@ import collections import binascii import tools from tools.translate import _ +from crm import wizard + +wizard.mail_compose_message.SUPPORTED_MODELS.append('hr.applicant') AVAILABLE_STATES = [ ('draft', 'New'), @@ -90,7 +93,7 @@ class hr_applicant(crm.crm_case, osv.osv): _name = "hr.applicant" _description = "Applicant" _order = "id desc" - _inherit = ['mailgate.thread'] + _inherit = ['mail.thread'] def _compute_day(self, cr, uid, ids, fields, args, context=None): """ @@ -128,7 +131,7 @@ class hr_applicant(crm.crm_case, osv.osv): _columns = { 'name': fields.char('Name', size=128, required=True), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), 'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the case without removing it."), 'description': fields.text('Description'), 'email_from': fields.char('Email', size=128, help="These people will receive email."), @@ -319,22 +322,13 @@ class hr_applicant(crm.crm_case, osv.osv): value = self.pool.get("survey").action_print_survey(cr, uid, ids, context=context) return value - def message_new(self, cr, uid, msg, context=None): - """ - Automatically calls when new email message arrives - - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks - """ - mailgate_pool = self.pool.get('email.server.tools') - attach_obj = self.pool.get('ir.attachment') - + def message_new(self, cr, uid, msg, custom_values=None, context=None): + """Automatically called when new email message arrives""" + res_id = super(hr_applicant,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) subject = msg.get('subject') or _("No Subject") - body = msg.get('body') + body = msg.get('body_text') msg_from = msg.get('from') priority = msg.get('priority') - vals = { 'name': subject, 'email_from': msg_from, @@ -342,42 +336,19 @@ class hr_applicant(crm.crm_case, osv.osv): 'description': body, 'user_id': False, } - if msg.get('priority', False): + if priority: vals['priority'] = priority + vals.update(self.message_partner_by_email(cr, uid, msg.get('from', False))) + self.write(cr, uid, [res_id], vals, context) + return res_id - res = mailgate_pool.get_partner(cr, uid, msg.get('from')) - if res: - vals.update(res) - res = self.create(cr, uid, vals, context=context) - - attachents = msg.get('attachments', []) - for attactment in attachents or []: - data_attach = { - 'name': attactment, - 'datas':binascii.b2a_base64(str(attachents.get(attactment))), - 'datas_fname': attactment, - 'description': 'Mail attachment', - 'res_model': self._name, - 'res_id': res, - } - attach_obj.create(cr, uid, data_attach, context=context) - - return res - - def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context=None): - """ - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of update mail’s IDs - """ - + def message_update(self, cr, uid, ids, msg, vals={}, default_act='pending', context=None): if isinstance(ids, (str, int, long)): ids = [ids] msg_from = msg['from'] vals.update({ - 'description': msg['body'] + 'description': msg['body_text'] }) if msg.get('priority', False): vals['priority'] = msg.get('priority') @@ -388,7 +359,7 @@ class hr_applicant(crm.crm_case, osv.osv): 'probability':'probability' } vls = { } - for line in msg['body'].split('\n'): + for line in msg['body_text'].split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower(), False): @@ -397,19 +368,9 @@ class hr_applicant(crm.crm_case, osv.osv): vals.update(vls) res = self.write(cr, uid, ids, vals, context=context) + self.message_append_dict(cr, uid, ids, msg, context=context) return res - def msg_send(self, cr, uid, id, *args, **argv): - """ Send The Message - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of email’s IDs - @param *args: Return Tuple Value - @param **args: Return Dictionary of Keyword Value - """ - return True - def case_open(self, cr, uid, ids, *args): """ @param self: The object pointer diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index b1d957aa6dc267dd778878348eb4f5b23db5876a..629b1c68012b0e9a213649565745a5a1caf0ba5d 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -144,15 +144,15 @@ <group colspan="4"> <field colspan="4" name="email_cc" string="Global CC" widget="char"/> </group> - <field name="message_ids" colspan="4" nolabel="1" mode="tree,form"> + <field name="message_ids" colspan="4" nolabel="1" mode="tree,form" readonly="1"> <tree string="History"> <field name="display_text" string="History Information"/> - <field name="history" invisible="1"/> - <button - string="Reply" attrs="{'invisible': [('history', '!=', True)]}" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'hr.applicant', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <field name="email_from" invisible="1"/> + <button + string="Reply" attrs="{'invisible': [('email_from', '=', False)]}" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action" /> </tree> <form string="History"> <group col="4" colspan="4"> @@ -160,20 +160,18 @@ <field name="date"/> <field name="email_to" widget="char" size="512"/> <field name="email_cc" widget="char" size="512"/> - <field name="name" colspan="4" widget="char" size="512"/> - <field name="history" invisible="1"/> + <field name="subject" colspan="4" widget="char" size="512"/> </group> <notebook colspan="4"> <page string="Details"> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <field name="description" colspan="4" nolabel="1" height="250"/> - <button colspan="4" - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'hr.applicant', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1" height="250"/> + <button colspan="4" string="Reply" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action"/> </group> - <group attrs="{'invisible': [('history', '=', True)]}"> + <group attrs="{'invisible': [('email_from', '!=', False)]}"> <field name="display_text" colspan="4" nolabel="1" height="250"/> </group> </page> @@ -183,14 +181,13 @@ </notebook> </form> </field> - <button string="Add Internal Note" + <button string="Add Internal Note" name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action" /> - <button string="Send New Email" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'hr.applicant'}" - icon="terp-mail-message-new" type="action" /> + <button string="Send New Email" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action"/> </page> <page string="Notes"> <field name="description" nolabel="1" colspan="4"/> diff --git a/addons/hr_recruitment/security/ir.model.access.csv b/addons/hr_recruitment/security/ir.model.access.csv index 2907b96fd81a675d210bb03fe8fb52508854002c..96baaf9e278e7e9da233ba6f38559e570e723909 100644 --- a/addons/hr_recruitment/security/ir.model.access.csv +++ b/addons/hr_recruitment/security/ir.model.access.csv @@ -3,7 +3,7 @@ "access_hr_recruitment_report","hr.recruitment.report","model_hr_recruitment_report","base.group_hr_manager",1,1,1,1 "access_hr_recruitment_stage_user","hr.recruitment.stage.user","model_hr_recruitment_stage","base.group_hr_user",1,1,1,1 "access_hr_recruitment_degree","hr.recruitment.degree","model_hr_recruitment_degree","base.group_hr_user",1,1,1,1 -"access_mailgate_message_user","mailgate.message.user","mail_gateway.model_mailgate_message","base.group_hr_user",1,1,1,1 +"access_mail_message_user","mail.message.user","mail.model_mail_message","base.group_hr_user",1,1,1,1 "access_res_partner_hr_user","res.partner.user","base.model_res_partner","base.group_hr_user",1,1,1,1 "access_res_partner_address_hr_user","res.partner.address.user","base.model_res_partner_address","base.group_hr_user",1,1,1,1 "access_survey_hr_user","survey.hr.user","survey.model_survey","base.group_hr_user",1,1,1,0 diff --git a/addons/hr_recruitment/wizard/hr_recruitment_phonecall.py b/addons/hr_recruitment/wizard/hr_recruitment_phonecall.py index edd08b5b5316750d0d7434b44a51660dfefbade4..cd9cbea52f7d725dc827995a1cc7be7c33204e43 100644 --- a/addons/hr_recruitment/wizard/hr_recruitment_phonecall.py +++ b/addons/hr_recruitment/wizard/hr_recruitment_phonecall.py @@ -32,7 +32,7 @@ class job2phonecall(osv.osv_memory): 'deadline': fields.datetime('Planned Date'), 'note': fields.text('Goals'), 'category_id': fields.many2one('crm.case.categ', 'Category', required=True), - } + } def _date_user(self, cr, uid, context=None): case_obj = self.pool.get('hr.applicant') @@ -50,7 +50,7 @@ class job2phonecall(osv.osv_memory): return categ_id and categ_id[0] or case.categ_id and case.categ_id.id or False def _get_note(self, cr, uid, context=None): - msg_obj = self.pool.get('mailgate.message') + mail_message = self.pool.get('mail.message') if context is None: context = {} if context.get('active_id'): @@ -58,9 +58,9 @@ class job2phonecall(osv.osv_memory): if case.description: return case.description else: - msg_ids = msg_obj.search(cr, uid, [('model', '=', 'hr.applicant'), ('res_id', '=', case.id), ('email_from', '!=', ''), ('email_to', '!=', '')], limit=1) + msg_ids = mail_message.search(cr, uid, [('model', '=', 'hr.applicant'), ('res_id', '=', case.id), ('email_from', '!=', ''), ('email_to', '!=', '')], limit=1) if msg_ids: - return msg_obj.browse(cr, uid, msg_ids[0], context=context).description + return mail_message.browse(cr, uid, msg_ids[0], context=context).body_text return False diff --git a/addons/html_view/i18n/mk.po b/addons/html_view/i18n/mk.po new file mode 100644 index 0000000000000000000000000000000000000000..dd98a0f9818d8647cf4fe3d9d96ac7c3ba93b35e --- /dev/null +++ b/addons/html_view/i18n/mk.po @@ -0,0 +1,63 @@ +# Macedonian translation for openobject-addons +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2011-01-11 11:15+0000\n" +"PO-Revision-Date: 2011-09-14 08:15+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Macedonian <mk@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: 2011-09-15 04:47+0000\n" +"X-Generator: Launchpad (build 13921)\n" + +#. module: html_view +#: field:html.view,name:0 +msgid "Name" +msgstr "Име" + +#. module: html_view +#: field:html.view,comp_id:0 +msgid "Company" +msgstr "Компанија" + +#. module: html_view +#: model:ir.actions.act_window,name:html_view.action_html_view_form +#: model:ir.ui.menu,name:html_view.html_form +msgid "Html Test" +msgstr "Html теÑÑ‚" + +#. module: html_view +#: view:html.view:0 +msgid "Html Example" +msgstr "Html пример" + +#. module: html_view +#: model:ir.module.module,shortdesc:html_view.module_meta_information +msgid "Html View" +msgstr "Html преглед" + +#. module: html_view +#: field:html.view,bank_ids:0 +msgid "Banks" +msgstr "Банки" + +#. module: html_view +#: model:ir.module.module,description:html_view.module_meta_information +msgid "" +"\n" +" This is the test module which shows html tag supports in normal xml form " +"view.\n" +" " +msgstr "" + +#. module: html_view +#: model:ir.model,name:html_view.model_html_view +msgid "html.view" +msgstr "html.view" diff --git a/addons/knowledge/i18n/en_GB.po b/addons/knowledge/i18n/en_GB.po index 8d91cb25c17c11ceb1486a902c0f211e411d23c8..6b24862c1f242595796fd5fd13dfa9db03cf95a5 100644 --- a/addons/knowledge/i18n/en_GB.po +++ b/addons/knowledge/i18n/en_GB.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-01-11 11:15+0000\n" -"PO-Revision-Date: 2011-08-25 17:36+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"PO-Revision-Date: 2011-09-13 09:03+0000\n" +"Last-Translator: John Bradshaw <Unknown>\n" "Language-Team: English (United Kingdom) <en_GB@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: 2011-09-05 05:50+0000\n" -"X-Generator: Launchpad (build 13830)\n" +"X-Launchpad-Export-Date: 2011-09-14 04:41+0000\n" +"X-Generator: Launchpad (build 13921)\n" #. module: knowledge #: model:ir.ui.menu,name:knowledge.menu_document @@ -94,7 +94,7 @@ msgstr "Configuration" #. module: knowledge #: model:ir.actions.act_window,name:knowledge.action_knowledge_installer msgid "Knowledge Modules Installation" -msgstr "" +msgstr "Knowledge Modules Installation" #. module: knowledge #: field:knowledge.installer,wiki_quality_manual:0 diff --git a/addons/mail_gateway/__init__.py b/addons/mail/__init__.py similarity index 89% rename from addons/mail_gateway/__init__.py rename to addons/mail/__init__.py index 6526541a068bc4316450cb7edf99f6e361a8437e..4787883ef9c3b182883e17dacf8eefc1252a81ad 100644 --- a/addons/mail_gateway/__init__.py +++ b/addons/mail/__init__.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). +# Copyright (C) 2009-Today OpenERP SA (<http://www.openerp.com>). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -19,8 +19,10 @@ # ############################################################################## -import mail_gateway +import mail_message +import mail_thread import res_partner +import wizard # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py new file mode 100644 index 0000000000000000000000000000000000000000..e9716be9e8e644ca16eedced2dfe5e4ba519ab09 --- /dev/null +++ b/addons/mail/__openerp__.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP S.A. (<http://www.openerp.com>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +{ + 'name': 'Email Subsystem', + 'version': '1.0', + 'category': 'Tools', + 'complexity': "easy", + 'description': """ +A generic email subsystem with message storage and queuing +========================================================== + +This email subsystem is not intended to be used as as standalone +application, but to provide a unified email abstraction that all +other applications can use. + +The main features are: + + * Relies on the global Outgoing Mail Servers configured in the + Administration menu for delivering outgoing mail + * Provides an API for sending messages and archiving them, + grouped by conversation + * Any OpenERP document can act as a conversation topic, provided + it includes the necessary support for handling incoming emails + (see the ``mail.thread`` class for more details). + * Includes queuing mechanism with automated configurable + scheduler-based processing + * Includes a generic email composition assistant, that can turn + into a mass-mailing assistant, and is capable of interpreting + simple *placeholder expressions* that will be replaced with + dynamic data when each email is actually sent. + This generic assistant is easily extensible to provide advanced + features (see ``email_template`` for example, which adds email + templating features to this assistant) + + """, + 'author': 'OpenERP SA', + 'website': 'http://www.openerp.com', + 'depends': ['base', 'base_tools'], + 'data': [ + "wizard/mail_compose_message_view.xml", + "mail_message_view.xml", + "mail_thread_view.xml", + "res_partner_view.xml", + 'security/ir.model.access.csv', + 'mail_data.xml', + ], + 'installable': True, + 'active': False, + 'certificate': '001056784984222247309', + 'images': ['images/customer_history.jpeg','images/messages_form.jpeg','images/messages_list.jpeg'], +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail_gateway/i18n/bg.po b/addons/mail/i18n/bg.po similarity index 100% rename from addons/mail_gateway/i18n/bg.po rename to addons/mail/i18n/bg.po diff --git a/addons/mail_gateway/i18n/ca.po b/addons/mail/i18n/ca.po similarity index 100% rename from addons/mail_gateway/i18n/ca.po rename to addons/mail/i18n/ca.po diff --git a/addons/mail_gateway/i18n/de.po b/addons/mail/i18n/de.po similarity index 100% rename from addons/mail_gateway/i18n/de.po rename to addons/mail/i18n/de.po diff --git a/addons/mail_gateway/i18n/es.po b/addons/mail/i18n/es.po similarity index 100% rename from addons/mail_gateway/i18n/es.po rename to addons/mail/i18n/es.po diff --git a/addons/mail_gateway/i18n/es_PY.po b/addons/mail/i18n/es_PY.po similarity index 100% rename from addons/mail_gateway/i18n/es_PY.po rename to addons/mail/i18n/es_PY.po diff --git a/addons/mail_gateway/i18n/fi.po b/addons/mail/i18n/fi.po similarity index 100% rename from addons/mail_gateway/i18n/fi.po rename to addons/mail/i18n/fi.po diff --git a/addons/mail_gateway/i18n/fr.po b/addons/mail/i18n/fr.po similarity index 100% rename from addons/mail_gateway/i18n/fr.po rename to addons/mail/i18n/fr.po diff --git a/addons/mail_gateway/i18n/gl.po b/addons/mail/i18n/gl.po similarity index 100% rename from addons/mail_gateway/i18n/gl.po rename to addons/mail/i18n/gl.po diff --git a/addons/mail_gateway/i18n/hu.po b/addons/mail/i18n/hu.po similarity index 100% rename from addons/mail_gateway/i18n/hu.po rename to addons/mail/i18n/hu.po diff --git a/addons/mail_gateway/i18n/it.po b/addons/mail/i18n/it.po similarity index 100% rename from addons/mail_gateway/i18n/it.po rename to addons/mail/i18n/it.po diff --git a/addons/mail_gateway/i18n/lt.po b/addons/mail/i18n/lt.po similarity index 100% rename from addons/mail_gateway/i18n/lt.po rename to addons/mail/i18n/lt.po diff --git a/addons/mail_gateway/i18n/lv.po b/addons/mail/i18n/lv.po similarity index 100% rename from addons/mail_gateway/i18n/lv.po rename to addons/mail/i18n/lv.po diff --git a/addons/mail_gateway/i18n/mail_gateway.pot b/addons/mail/i18n/mail.pot similarity index 100% rename from addons/mail_gateway/i18n/mail_gateway.pot rename to addons/mail/i18n/mail.pot diff --git a/addons/mail_gateway/i18n/nl.po b/addons/mail/i18n/nl.po similarity index 100% rename from addons/mail_gateway/i18n/nl.po rename to addons/mail/i18n/nl.po diff --git a/addons/mail_gateway/i18n/pl.po b/addons/mail/i18n/pl.po similarity index 100% rename from addons/mail_gateway/i18n/pl.po rename to addons/mail/i18n/pl.po diff --git a/addons/mail_gateway/i18n/pt.po b/addons/mail/i18n/pt.po similarity index 100% rename from addons/mail_gateway/i18n/pt.po rename to addons/mail/i18n/pt.po diff --git a/addons/mail_gateway/i18n/pt_BR.po b/addons/mail/i18n/pt_BR.po similarity index 100% rename from addons/mail_gateway/i18n/pt_BR.po rename to addons/mail/i18n/pt_BR.po diff --git a/addons/mail_gateway/i18n/ro.po b/addons/mail/i18n/ro.po similarity index 100% rename from addons/mail_gateway/i18n/ro.po rename to addons/mail/i18n/ro.po diff --git a/addons/mail_gateway/i18n/ru.po b/addons/mail/i18n/ru.po similarity index 100% rename from addons/mail_gateway/i18n/ru.po rename to addons/mail/i18n/ru.po diff --git a/addons/mail_gateway/i18n/sl.po b/addons/mail/i18n/sl.po similarity index 100% rename from addons/mail_gateway/i18n/sl.po rename to addons/mail/i18n/sl.po diff --git a/addons/mail_gateway/i18n/sr@latin.po b/addons/mail/i18n/sr@latin.po similarity index 100% rename from addons/mail_gateway/i18n/sr@latin.po rename to addons/mail/i18n/sr@latin.po diff --git a/addons/mail_gateway/i18n/sv.po b/addons/mail/i18n/sv.po similarity index 100% rename from addons/mail_gateway/i18n/sv.po rename to addons/mail/i18n/sv.po diff --git a/addons/mail_gateway/i18n/zh_CN.po b/addons/mail/i18n/zh_CN.po similarity index 100% rename from addons/mail_gateway/i18n/zh_CN.po rename to addons/mail/i18n/zh_CN.po diff --git a/addons/email_template/email_template_scheduler_data.xml b/addons/mail/mail_data.xml similarity index 59% rename from addons/email_template/email_template_scheduler_data.xml rename to addons/mail/mail_data.xml index 45a9be04ac7ac16a0be6f6e50e3c0a15fd0dbaf7..964552b951ac030755aa46cd7ec4f31bfc8fb968 100644 --- a/addons/email_template/email_template_scheduler_data.xml +++ b/addons/mail/mail_data.xml @@ -1,15 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> <openerp> <data noupdate="1"> - <record forcecreate="True" id="ir_cron_mail_scheduler_action" model="ir.cron"> - <field name="name">Email Template scheduler</field> + <record forcecreate="True" id="ir_cron_mail_scheduler_action" model="ir.cron"> + <field name="name">Email Queue Manager</field> <field name="user_id" ref="base.user_root"/> <field name="interval_number">1</field> <field name="interval_type">hours</field> <field name="numbercall">-1</field> <field eval="False" name="doall"/> - <field eval="'email_template.mailbox'" name="model"/> - <field eval="'run_mail_scheduler'" name="function"/> + <field eval="'mail.message'" name="model"/> + <field eval="'process_email_queue'" name="function"/> <field eval="'()'" name="args"/> </record> </data> diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py new file mode 100644 index 0000000000000000000000000000000000000000..621eef59da58f4c48f7cf2793b0528c1e1d743a8 --- /dev/null +++ b/addons/mail/mail_message.py @@ -0,0 +1,522 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-2011 OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +import base64 +import dateutil.parser +import email +import logging +import re +import time +from email.header import decode_header +from email.message import Message + +import tools +from osv import osv +from osv import fields +from tools.translate import _ +from tools.safe_eval import literal_eval + +_logger = logging.getLogger('mail') + +def format_date_tz(date, tz=None): + if not date: + return 'n/a' + format = tools.DEFAULT_SERVER_DATETIME_FORMAT + return tools.server_to_local_timestamp(date, format, format, tz) + +def truncate_text(text): + lines = text and text.split('\n') or [] + if len(lines) > 3: + res = '\n\t'.join(lines[:3]) + '...' + else: + res = '\n\t'.join(lines) + return res + +def decode(text): + """Returns unicode() string conversion of the the given encoded smtp header text""" + if text: + text = decode_header(text.replace('\r', '')) + return ''.join([tools.ustr(x[0], x[1]) for x in text]) + +def to_email(text): + """Return a list of the email addresses found in ``text``""" + if not text: return [] + return re.findall(r'([^ ,<@]+@[^> ,]+)', text) + +class mail_message_common(osv.osv_memory): + """Common abstract class for holding the main attributes of a + message object. It could be reused as parent model for any + database model or wizard screen that needs to hold a kind of + message""" + + _name = 'mail.message.common' + _rec_name = 'subject' + _columns = { + 'subject': fields.char('Subject', size=512, required=True), + 'model': fields.char('Related Document model', size=128, select=1, readonly=1), + 'res_id': fields.integer('Related Document ID', select=1, readonly=1), + 'date': fields.datetime('Date'), + 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences'), + 'email_to': fields.char('To', size=256, help='Message recipients'), + 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), + 'email_bcc': fields.char('Bcc', size=256, help='Blind carbon copy message recipients'), + 'reply_to':fields.char('Reply-To', size=256, help='Preferred response address for the message'), + 'headers': fields.text('Message headers', readonly=1, + help="Full message headers, e.g. SMTP session headers " + "(usually available on inbound messages only)"), + 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), + 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), + 'subtype': fields.char('Message type', size=32, help="Type of message, usually 'html' or 'plain', used to " + "select plaintext or rich text contents accordingly", readonly=1), + 'body_text': fields.text('Text contents', help="Plain-text version of the message"), + 'body_html': fields.text('Rich-text contents', help="Rich-text/HTML version of the message"), + } + + _defaults = { + 'subtype': 'plain' + } + +class mail_message(osv.osv): + '''Model holding RFC2822 email messages, and providing facilities + to parse, queue and send new messages + + Messages that do not have a value for the email_from column + are simple log messages (e.g. document state changes), while + actual e-mails have the email_from value set. + The ``display_text`` field will have a slightly different + presentation for real emails and for log messages. + ''' + + _name = 'mail.message' + _inherit = 'mail.message.common' + _description = 'Email Message' + _order = 'date desc' + + # XXX to review - how to determine action to use? + def open_document(self, cr, uid, ids, context=None): + action_data = False + if ids: + msg = self.browse(cr, uid, ids[0], context=context) + model = msg.model + res_id = msg.res_id + + ir_act_window = self.pool.get('ir.actions.act_window') + action_ids = ir_act_window.search(cr, uid, [('res_model', '=', model)]) + if action_ids: + action_data = ir_act_window.read(cr, uid, action_ids[0], context=context) + action_data.update({ + 'domain' : "[('id','=',%d)]"%(res_id), + 'nodestroy': True, + 'context': {} + }) + return action_data + + # XXX to review - how to determine action to use? + def open_attachment(self, cr, uid, ids, context=None): + action_data = False + action_pool = self.pool.get('ir.actions.act_window') + message = self.browse(cr, uid, ids, context=context)[0] + att_ids = [x.id for x in message.attachment_ids] + action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')]) + if action_ids: + action_data = action_pool.read(cr, uid, action_ids[0], context=context) + action_data.update({ + 'domain': [('id','in',att_ids)], + 'nodestroy': True + }) + return action_data + + def _get_display_text(self, cr, uid, ids, name, arg, context=None): + if context is None: + context = {} + tz = context.get('tz') + result = {} + for message in self.browse(cr, uid, ids, context=context): + msg_txt = '' + if message.email_from: + msg_txt += _('%s wrote on %s: \n Subject: %s \n\t') % (message.email_from or '/', format_date_tz(message.date, tz), message.subject) + if message.body_text: + msg_txt += truncate_text(message.body_text) + else: + msg_txt = (message.user_id.name or '/') + _(' on ') + format_date_tz(message.date, tz) + ':\n\t' + msg_txt += message.subject + result[message.id] = msg_txt + return result + + _columns = { + 'partner_id': fields.many2one('res.partner', 'Related partner'), + 'user_id': fields.many2one('res.users', 'Related user', readonly=1), + 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'), + 'display_text': fields.function(_get_display_text, method=True, type='text', size="512", string='Display Text'), + 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), + 'state': fields.selection([ + ('outgoing', 'Outgoing'), + ('sent', 'Sent'), + ('received', 'Received'), + ('exception', 'Delivery Failed'), + ('cancel', 'Cancelled'), + ], 'State', readonly=True), + 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"), + 'original': fields.binary('Original', help="Original version of the message, as it was sent on the network", readonly=1), + } + + _defaults = { + 'state': 'received', + } + + def init(self, cr): + cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'mail_message_model_res_id_idx'""") + if not cr.fetchone(): + cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""") + + def copy(self, cr, uid, id, default=None, context=None): + """Overridden to avoid duplicating fields that are unique to each email""" + if default is None: + default = {} + default.update(message_id=False,original=False,headers=False) + return super(mail_message,self).copy(cr, uid, id, default=default, context=context) + + def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, email_cc=None, + email_bcc=None, reply_to=False, attachments=None, message_id=False, references=False, + res_id=False, subtype='plain', headers=None, mail_server_id=False, auto_delete=False, + context=None): + """Schedule sending a new email message, to be sent the next time the mail scheduler runs, or + the next time :meth:`process_email_queue` is called explicitly. + + :param string email_from: sender email address + :param list email_to: list of recipient addresses (to be joined with commas) + :param string subject: email subject (no pre-encoding/quoting necessary) + :param string body: email body, according to the ``subtype`` (by default, plaintext). + If html subtype is used, the message will be automatically converted + to plaintext and wrapped in multipart/alternative. + :param list email_cc: optional list of string values for CC header (to be joined with commas) + :param list email_bcc: optional list of string values for BCC header (to be joined with commas) + :param string model: optional model name of the document this mail is related to (this will also + be used to generate a tracking id, used to match any response related to the + same document) + :param int res_id: optional resource identifier this mail is related to (this will also + be used to generate a tracking id, used to match any response related to the + same document) + :param string reply_to: optional value of Reply-To header + :param string subtype: optional mime subtype for the text body (usually 'plain' or 'html'), + must match the format of the ``body`` parameter. Default is 'plain', + making the content part of the mail "text/plain". + :param dict attachments: map of filename to filecontents, where filecontents is a string + containing the bytes of the attachment + :param dict headers: optional map of headers to set on the outgoing mail (may override the + other headers, including Subject, Reply-To, Message-Id, etc.) + :param int mail_server_id: optional id of the preferred outgoing mail server for this mail + :param bool auto_delete: optional flag to turn on auto-deletion of the message after it has been + successfully sent (default to False) + + """ + if context is None: + context = {} + if attachments is None: + attachments = {} + attachment_obj = self.pool.get('ir.attachment') + for param in (email_to, email_cc, email_bcc): + if param and not isinstance(param, list): + param = [param] + msg_vals = { + 'subject': subject, + 'date': time.strftime('%Y-%m-%d %H:%M:%S'), + 'user_id': uid, + 'model': model, + 'res_id': res_id, + 'body_text': body if subtype != 'html' else False, + 'body_html': body if subtype == 'html' else False, + 'email_from': email_from, + 'email_to': email_to and ','.join(email_to) or '', + 'email_cc': email_cc and ','.join(email_cc) or '', + 'email_bcc': email_bcc and ','.join(email_bcc) or '', + 'reply_to': reply_to, + 'message_id': message_id, + 'references': references, + 'subtype': subtype, + 'headers': headers, # serialize the dict on the fly + 'mail_server_id': mail_server_id, + 'state': 'outgoing', + 'auto_delete': auto_delete + } + email_msg_id = self.create(cr, uid, msg_vals, context) + attachment_ids = [] + for fname, fcontent in attachments.iteritems(): + attachment_data = { + 'name': fname, + 'datas_fname': fname, + 'datas': fcontent, + 'res_model': self._name, + 'res_id': email_msg_id, + } + if context.has_key('default_type'): + del context['default_type'] + attachment_ids.append(attachment_obj.create(cr, uid, attachment_data, context)) + if attachment_ids: + self.write(cr, uid, email_msg_id, { 'attachment_ids': [(6, 0, attachment_ids)]}, context=context) + return email_msg_id + + def mark_outgoing(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state':'outgoing'}, context) + + def process_email_queue(self, cr, uid, ids=None, context=None): + """Send immediately queued messages, committing after each + message is sent - this is not transactional and should + not be called during another transaction! + + :param list ids: optional list of emails ids to send. If passed + no search is performed, and these ids are used + instead. + :param dict context: if a 'filters' key is present in context, + this value will be used as an additional + filter to further restrict the outgoing + messages to send (by default all 'outgoing' + messages are sent). + """ + if context is None: + context = {} + if not ids: + filters = [('state', '=', 'outgoing')] + if 'filters' in context: + filters.extend(context['filters']) + ids = self.search(cr, uid, filters, context=context) + res = None + try: + # Force auto-commit - this is meant to be called by + # the scheduler, and we can't allow rolling back the status + # of previously sent emails! + res = self.send(cr, uid, ids, auto_commit=True, context=context) + except Exception: + _logger.exception("Failed processing mail queue") + return res + + def parse_message(self, message, save_original=False): + """Parses a string or email.message.Message representing an + RFC-2822 email, and returns a generic dict holding the + message details. + + :param message: the message to parse + :type message: email.message.Message | string | unicode + :param bool save_original: whether the returned dict + should include an ``original`` entry with the base64 + encoded source of the message. + :rtype: dict + :return: A dict with the following structure, where each + field may not be present if missing in original + message:: + + { 'message-id': msg_id, + 'subject': subject, + 'from': from, + 'to': to, + 'cc': cc, + 'headers' : { 'X-Mailer': mailer, + #.. all X- headers... + }, + 'subtype': msg_mime_subtype, + 'body_text': plaintext_body + 'body_html': html_body, + 'attachments': { 'file1': 'bytes', + 'file2': 'bytes' } + # ... + 'original': source_of_email, + } + """ + msg_txt = message + if isinstance(message, str): + msg_txt = email.message_from_string(message) + + # Warning: message_from_string doesn't always work correctly on unicode, + # we must use utf-8 strings here :-( + if isinstance(message, unicode): + message = message.encode('utf-8') + msg_txt = email.message_from_string(message) + + message_id = msg_txt.get('message-id', False) + msg = {} + + if save_original: + # save original, we need to be able to read the original email sometimes + msg['original'] = message.as_string() if isinstance(message, Message) \ + else message + msg['original'] = base64.b64encode(msg['original']) # binary fields are b64 + + if not message_id: + # Very unusual situation, be we should be fault-tolerant here + message_id = time.time() + msg_txt['message-id'] = message_id + _logger.info('Parsing Message without message-id, generating a random one: %s', message_id) + + fields = msg_txt.keys() + msg['id'] = message_id + msg['message-id'] = message_id + + if 'Subject' in fields: + msg['subject'] = decode(msg_txt.get('Subject')) + + if 'Content-Type' in fields: + msg['content-type'] = msg_txt.get('Content-Type') + + if 'From' in fields: + msg['from'] = decode(msg_txt.get('From') or msg_txt.get_unixfrom()) + + if 'To' in fields: + msg['to'] = decode(msg_txt.get('To')) + if 'Delivered-To' in fields: + msg['to'] = decode(msg_txt.get('Delivered-To')) + + if 'CC' in fields: + msg['cc'] = decode(msg_txt.get('CC')) + + if 'Reply-To' in fields: + msg['reply'] = decode(msg_txt.get('Reply-To')) + + if 'Date' in fields: + date_hdr = decode(msg_txt.get('Date')) + msg['date'] = dateutil.parser.parse(date_hdr).strftime("%Y-%m-%d %H:%M:%S") + + if 'Content-Transfer-Encoding' in fields: + msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') + + if 'References' in fields: + msg['references'] = msg_txt.get('References') + + if 'In-Reply-To' in fields: + msg['in-reply-to'] = msg_txt.get('In-Reply-To') + + msg['headers'] = {} + msg['subtype'] = 'plain' + for item in msg_txt.items(): + if item[0].startswith('X-'): + msg['headers'].update({item[0]: item[1]}) + if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', ''): + encoding = msg_txt.get_content_charset() + body = msg_txt.get_payload(decode=True) + if 'text/html' in msg.get('content-type', ''): + msg['body_html'] = body + msg['subtype'] = 'html' + body = tools.html2plaintext(body) + msg['body_text'] = tools.ustr(body, encoding) + + attachments = {} + if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', ''): + body = "" + if 'multipart/alternative' in msg.get('content-type', ''): + msg['subtype'] = 'alternative' + else: + msg['subtype'] = 'mixed' + for part in msg_txt.walk(): + if part.get_content_maintype() == 'multipart': + continue + + encoding = part.get_content_charset() + filename = part.get_filename() + if part.get_content_maintype()=='text': + content = part.get_payload(decode=True) + if filename: + attachments[filename] = content + content = tools.ustr(content, encoding) + if part.get_content_subtype() == 'html': + msg['body_html'] = content + msg['subtype'] = 'html' # html version prevails + body = tools.ustr(tools.html2plaintext(content)) + elif part.get_content_subtype() == 'plain': + body = content + elif part.get_content_maintype() in ('application', 'image'): + if filename : + attachments[filename] = part.get_payload(decode=True) + else: + res = part.get_payload(decode=True) + body += tools.ustr(res, encoding) + + msg['body_text'] = body + msg['attachments'] = attachments + + # for backwards compatibility: + msg['body'] = msg['body_text'] + msg['sub_type'] = msg['subtype'] or 'plain' + return msg + + def send(self, cr, uid, ids, auto_commit=False, context=None): + """Sends the selected emails immediately, ignoring their current + state (mails that have already been sent should not be passed + unless they should actually be re-sent). + Emails successfully delivered are marked as 'sent', and those + that fail to be deliver are marked as 'exception', and the + corresponding error message is output in the server logs. + + :param bool auto_commit: whether to force a commit of the message + status after sending each message (meant + only for processing by the scheduler), + should never be True during normal + transactions (default: False) + :return: True + """ + if context is None: + context = {} + ir_mail_server = self.pool.get('ir.mail_server') + self.write(cr, uid, ids, {'state': 'outgoing'}, context=context) + for message in self.browse(cr, uid, ids, context=context): + try: + attachments = [] + for attach in message.attachment_ids: + attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) + msg = ir_mail_server.build_email( + email_from=message.email_from, + email_to=to_email(message.email_to), + subject=message.subject, + body=message.body_html if message.subtype == 'html' else message.body_text, + email_cc=to_email(message.email_cc), + email_bcc=to_email(message.email_bcc), + reply_to=message.reply_to, + attachments=attachments, message_id=message.message_id, + references = message.references, + object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)), + subtype=message.subtype, + headers=message.headers and literal_eval(message.headers)) + res = ir_mail_server.send_email(cr, uid, msg, + mail_server_id=message.mail_server_id.id, + context=context) + if res: + message.write({'state':'sent', 'message_id': res}) + else: + message.write({'state':'exception'}) + + # if auto_delete=True then delete that sent messages as well as attachments + message.refresh() + if message.state == 'sent' and message.auto_delete: + self.pool.get('ir.attachment').unlink(cr, uid, + [x.id for x in message.attachment_ids], + context=context) + message.unlink() + except Exception: + _logger.exception('failed sending mail.message %s', message.id) + message.write({'state':'exception'}) + + if auto_commit == True: + cr.commit() + return True + + def cancel(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'state':'cancel'}, context=context) + return True + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..83e7474fe1a3ff8f910348266576f8784b2c0c19 --- /dev/null +++ b/addons/mail/mail_message_view.xml @@ -0,0 +1,155 @@ +<?xml version="1.0"?> +<openerp> + <data> + + <menuitem name="Configuration" parent="base.menu_tools" id="base.menu_lunch_survey_root" sequence="20"/> + + <record model="ir.ui.view" id="view_email_message_form"> + <field name="name">mail.message.form</field> + <field name="model">mail.message</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Email message"> + <group colspan="4" col="6"> + <field name="subject" colspan="4"/> + <field name="date"/> + <field name="user_id" string="User"/> + <field name="partner_id" readonly="1" attrs="{'invisible':[('partner_id', '=', False)]}"/> + </group> + <notebook colspan="4"> + <page string="Details"> + <group col="2" colspan="2"> + <separator string="Recipients" colspan="4"/> + <field name="email_from"/> + <field name="email_to"/> + <field name="email_cc" attrs="{'invisible':[('email_cc', '=', False)]}"/> + <field name="email_bcc" groups="base.group_extended" attrs="{'invisible':[('email_bcc', '=', False)]}"/> + <field name="reply_to" attrs="{'invisible':[('reply_to', '=', False)]}"/> + </group> + <group col="4" colspan="2"> + <separator string="Message Details" colspan="4"/> + <field name="model"/> + <button name="open_document" string="Open" type="object" icon="gtk-jump-to" colspan="2"/> + <field name="res_id" groups="base.group_extended"/> + <field name="message_id" groups="base.group_extended" colspan="4" attrs="{'invisible':[('message_id', '=', False)]}"/> + <field name="references" colspan="4" widget="char" size="512" groups="base.group_extended" attrs="{'invisible':[('references', '=', False)]}"/> + </group> + + <notebook colspan="4"> + <page string="Body (Rich)" attrs="{'invisible':[('subtype','=','plain')]}"> + <field name="body_html" widget="text_html" nolabel="1" colspan="4"/> + </page> + <page string="Body (Plain)"> + <field name="body_text" colspan="4" widget="text" nolabel="1"/> + </page> + </notebook> + + <separator string="" colspan="4"/> + <group col="6" colspan="4"> + <field name="state" colspan="2"/> + <group col="4" colspan="2"> + <button name="%(action_email_compose_message_wizard)d" string="Reply" type="action" icon="terp-mail-replied" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" states='received,sent,exception,cancel'/> + <button name="send" string="Send Now" type="object" icon="gtk-media-play" states='outgoing'/> + <button name="mark_outgoing" string="Retry" type="object" icon="gtk-redo" states='exception,cancel'/> + <button name="cancel" string="Cancel" type="object" icon="terp-gtk-stop" states='outgoing'/> + </group> + </group> + </page> + <page string="Attachments"> + <separator string="Attachments" colspan="4"/> + <field name="attachment_ids" nolabel="1" colspan="4"/> + </page> + <page string="Advanced" groups="base.group_extended"> + <group col="2" colspan="4"> + <field name="mail_server_id" attrs="{'invisible':[('mail_server_id', '=', False)]}"/> + <field name="subtype" attrs="{'invisible':[('subtype', '=', False)]}"/> + <field name="auto_delete"/> + <field name="headers" colspan="4" attrs="{'invisible':[('headers', '=', False)]}"/> + <field name="original" colspan="4" attrs="{'invisible':[('original', '=', False)]}"/> + </group> + </page> + </notebook> + </form> + </field> + </record> + + <record model="ir.ui.view" id="view_email_message_tree"> + <field name="name">mail.message.tree</field> + <field name="model">mail.message</field> + <field name="type">tree</field> + <field name="arch" type="xml"> + <tree string="Emails" colors="grey:state in ('sent', 'cancel');blue:state=='outgoing';red:state=='exception';black:state=='received'"> + <field name="date"/> + <field name="subject"/> + <field name="email_from"/> + <field name="user_id" string="User"/> + <field name="message_id" invisible="1"/> + <field name="partner_id" invisible="1"/> + <field name="state"/> + <button name="send" string="Send Now" type="object" icon="gtk-media-play" states='outgoing'/> + <button name="open_document" string="Open Related Document" type="object" icon="gtk-jump-to"/> + <button name="open_attachment" string="Open Attachments" type="object" icon="gtk-jump-to"/> + </tree> + </field> + </record> + + <record model="ir.ui.view" id="view_email_message_search"> + <field name="name">mail.message.search</field> + <field name="model">mail.message</field> + <field name="type">search</field> + <field name="arch" type="xml"> + <search string="Email Search"> + <filter icon="terp-camera_test" string="Received" domain="[('state','=','received')]"/> + <filter icon="terp-call-start" name="outgoing" string="Outgoing" domain="[('state','=','outgoing')]"/> + <filter icon="terp-gtk-stop" string="Failed" domain="[('state','=','exception')]"/> + <field name="email_from"/> + <field name="email_to"/> + <field name="subject"/> + <field name="date"/> + <newline/> + <group expand="0" string="Extended Filters..." groups="base.group_extended"> + <field name="user_id" string="User"/> + <field name="partner_id" string="Partner Name"/> + </group> + <newline/> + <group expand="0" string="Group By..." groups="base.group_extended"> + <filter string="State" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> + <filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/> + <filter string="User" name="User" icon="terp-personal" context="{'group_by':'user_id'}"/> + <separator orientation="vertical"/> + <filter string="Thread" icon="terp-mail-" domain="[]" context="{'group_by':'message_id'}"/> + <separator orientation="vertical"/> + <filter string="Month" help="Creation Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}"/> + </group> + </search> + </field> + </record> + + <record id="action_view_mail_message" model="ir.actions.act_window"> + <field name="name">Messages</field> + <field name="res_model">mail.message</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + <field name="domain">[('email_from', '!=', False)]</field> + <field name="context">{'search_default_outgoing':1}</field> + <field name="search_view_id" ref="view_email_message_search"/> + </record> + + <act_window domain="[('partner_id', '=', active_id), ('email_from', '!=', False)]" + id="act_res_partner_emails" name="Emails" + res_model="mail.message" + src_model="res.partner" + view_id="view_email_message_tree"/> + + <menuitem name="Emails" id="menu_email_message_tools" parent="base.menu_tools" /> + + <menuitem name="Messages" + id="menu_email_message" + parent="menu_email_message_tools" + action="action_view_mail_message" /> + + <menuitem name="Emails" id="menu_config_email" parent="base.menu_lunch_survey_root" sequence="20"/> + + </data> +</openerp> diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py new file mode 100644 index 0000000000000000000000000000000000000000..fa785c3517c116cf68e3ad77ba882cce43a94ffd --- /dev/null +++ b/addons/mail/mail_thread.py @@ -0,0 +1,463 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2009-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +import time +import tools +import base64 +import email +from email.utils import parsedate + +import logging +import xmlrpclib +from osv import osv, fields +from tools.translate import _ +from mail_message import decode, to_email + +_logger = logging.getLogger('mail') + +class mail_thread(osv.osv): + '''Mixin model, meant to be inherited by any model that needs to + act as a discussion topic on which messages can be attached. + Public methods are prefixed with ``message_`` in order to avoid + name collisions with methods of the models that will inherit + from this mixin. + + ``mail.thread`` adds a one2many of mail.messages, acting as the + thread's history, and a few methods that may be overridden to + implement model-specific behavior upon arrival of new messages. + + Inheriting classes are not required to implement any method, as the + default implementation will work for any model. However it is common + to override at least the ``message_new`` and ``message_update`` + methods (calling ``super``) to add model-specific behavior at + creation and update of a thread. + + ''' + _name = 'mail.thread' + _description = 'Email Thread' + + _columns = { + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', readonly=True), + } + + def message_thread_followers(self, cr, uid, ids, context=None): + """Returns a list of email addresses of the people following + this thread, including the sender of each mail, and the + people who were in CC of the messages, if any. + """ + res = {} + if isinstance(ids, (str, int, long)): + ids = [long(ids)] + for thread in self.browse(cr, uid, ids, context=context): + l = set() + for message in thread.message_ids: + l.add((message.user_id and message.user_id.user_email) or '') + l.add(message.email_from or '') + l.add(message.email_cc or '') + res[thread.id] = filter(None, l) + return res + + def copy(self, cr, uid, id, default=None, context=None): + """Overrides default copy method to empty the thread of + messages attached to this record, as the copied object + will have its own thread and does not have to share it. + """ + if default is None: + default = {} + default.update({ + 'message_ids': [], + }) + return super(mail_thread, self).copy(cr, uid, id, default, context=context) + + def message_new(self, cr, uid, msg_dict, custom_values=None, context=None): + """Called by ``message_process`` when a new message is received + for a given thread model, if the message did not belong to + an existing thread. + The default behavior is to create a new record of the corresponding + model (based on some very basic info extracted from the message), + then attach the message to the newly created record + (by calling ``message_append_dict``). + Additional behavior may be implemented by overriding this method. + + :param dict msg_dict: a map containing the email details and + attachments. See ``message_process`` and + ``mail.message.parse`` for details. + :param dict custom_values: optional dictionary of additional + field values to pass to create() + when creating the new thread record. + Be careful, these values may override + any other values coming from the message. + :param dict context: if a ``thread_model`` value is present + in the context, its value will be used + to determine the model of the record + to create (instead of the current model). + :rtype: int + :return: the id of the newly created thread object + """ + if context is None: + context = {} + model = context.get('thread_model') or self._name + model_pool = self.pool.get(model) + fields = model_pool.fields_get(cr, uid, context=context) + data = model_pool.default_get(cr, uid, fields, context=context) + if 'name' in fields and not data.get('name'): + data['name'] = msg_dict.get('from','') + if custom_values and isinstance(custom_values, dict): + data.update(custom_values) + res_id = model_pool.create(cr, uid, data, context=context) + self.message_append_dict(cr, uid, [res_id], msg_dict, context=context) + return res_id + + def message_update(self, cr, uid, ids, msg_dict, vals={}, default_act=None, context=None): + """Called by ``message_process`` when a new message is received + for an existing thread. The default behavior is to create a + new mail.message in the given thread (by calling + ``message_append_dict``) + Additional behavior may be implemented by overriding this + method. + + :param dict msg_dict: a map containing the email details and + attachments. See ``message_process`` and + ``mail.message.parse()`` for details. + :param dict context: if a ``thread_model`` value is present + in the context, its value will be used + to determine the model of the thread to + update (instead of the current model). + """ + return self.message_append_dict(cr, uid, ids, msg_dict, context=context) + + def message_append(self, cr, uid, threads, subject, body_text=None, email_to=False, + email_from=False, email_cc=None, email_bcc=None, reply_to=None, + email_date=None, message_id=False, references=None, + attachments=None, body_html=None, subtype=None, headers=None, + original=None, context=None): + """Creates a new mail.message attached to the current mail.thread, + containing all the details passed as parameters. All attachments + will be attached to the thread record as well as to the actual + message. + If only the ``threads`` and ``subject`` parameters are provided, + a *event log* message is created, without the usual envelope + attributes (sender, recipients, etc.). + + :param threads: list of thread ids, or list of browse_records representing + threads to which a new message should be attached + :param subject: subject of the message, or description of the event if this + is an *event log* entry. + :param email_to: Email-To / Recipient address + :param email_from: Email From / Sender address if any + :param email_cc: Comma-Separated list of Carbon Copy Emails To addresse if any + :param email_bcc: Comma-Separated list of Blind Carbon Copy Emails To addresses if any + :param reply_to: reply_to header + :param email_date: email date string if different from now, in server timezone + :param message_id: optional email identifier + :param references: optional email references + :param body_text: plaintext contents of the mail or log message + :param body_html: html contents of the mail or log message + :param subtype: optional type of message: 'plain' or 'html', corresponding to the main + body contents (body_text or body_html). + :param headers: mail headers to store + :param dict attachments: map of attachment filenames to binary contents, if any. + :param str original: optional full source of the RFC2822 email, for reference + :param dict context: if a ``thread_model`` value is present + in the context, its value will be used + to determine the model of the thread to + update (instead of the current model). + """ + if context is None: + context = {} + if attachments is None: + attachments = {} + + if email_date: + edate = parsedate(email_date) + if edate is not None: + email_date = time.strftime('%Y-%m-%d %H:%M:%S', edate) + + if all(isinstance(thread_id, (int, long)) for thread_id in threads): + model = context.get('thread_model') or self._name + model_pool = self.pool.get(model) + threads = model_pool.browse(cr, uid, threads, context=context) + + ir_attachment = self.pool.get('ir.attachment') + mail_message = self.pool.get('mail.message') + + for thread in threads: + to_attach = [] + for fname, fcontent in attachments.items(): + if isinstance(fcontent, unicode): + fcontent = fcontent.encode('utf-8') + data_attach = { + 'name': fname, + 'datas': base64.b64encode(str(fcontent)), + 'datas_fname': fname, + 'description': _('Mail attachment'), + 'res_model': thread._name, + 'res_id': thread.id, + } + to_attach.append(ir_attachment.create(cr, uid, data_attach, context=context)) + + partner_id = hasattr(thread, 'partner_id') and (thread.partner_id and thread.partner_id.id or False) or False + if not partner_id and thread._name == 'res.partner': + partner_id = thread.id + data = { + 'subject': subject, + 'user_id': uid, + 'model' : thread._name, + 'partner_id': partner_id, + 'res_id': thread.id, + 'date': time.strftime('%Y-%m-%d %H:%M:%S'), + 'message_id': message_id, + 'body_text': body_text or (hasattr(thread, 'description') and thread.description or False), + 'attachment_ids': [(6, 0, to_attach)], + 'state' : 'received', + } + + if email_from: + for param in (email_to, email_cc, email_bcc): + if isinstance(param, list): + param = ", ".join(param) + data = { + 'subject': subject or _('History'), + 'user_id': uid, + 'model' : thread._name, + 'res_id': thread.id, + 'date': email_date or time.strftime('%Y-%m-%d %H:%M:%S'), + 'body_text': body_text, + 'email_to': email_to, + 'email_from': email_from or \ + (hasattr(thread, 'user_id') and thread.user_id and thread.user_id.user_email), + 'email_cc': email_cc, + 'email_bcc': email_bcc, + 'partner_id': partner_id, + 'references': references, + 'message_id': message_id, + 'attachment_ids': [(6, 0, to_attach)], + 'state' : 'received', + 'body_html': body_html, + 'subtype': subtype, + 'headers': headers, + 'reply_to': reply_to, + 'original': original, + } + mail_message.create(cr, uid, data, context=context) + return True + + def message_append_dict(self, cr, uid, ids, msg_dict, context=None): + """Creates a new mail.message attached to the given threads (``ids``), + with the contents of ``msg_dict``, by calling ``message_append`` + with the mail details. All attachments in msg_dict will be + attached to the object record as well as to the actual + mail message. + + :param dict msg_dict: a map containing the email details and + attachments. See ``message_process()`` and + ``mail.message.parse()`` for details on + the dict structure. + :param dict context: if a ``thread_model`` value is present + in the context, its value will be used + to determine the model of the thread to + update (instead of the current model). + """ + return self.message_append(cr, uid, ids, + subject = msg_dict.get('subject'), + body_text = msg_dict.get('body_text'), + email_to = msg_dict.get('to'), + email_from = msg_dict.get('from'), + email_cc = msg_dict.get('cc'), + email_bcc = msg_dict.get('bcc'), + reply_to = msg_dict.get('reply'), + email_date = msg_dict.get('date'), + message_id = msg_dict.get('message-id'), + references = msg_dict.get('references')\ + or msg_dict.get('in-reply-to'), + attachments = msg_dict.get('attachments'), + body_html= msg_dict.get('body_html'), + subtype = msg_dict.get('subtype'), + headers = msg_dict.get('headers'), + original = msg_dict.get('original'), + context = context) + + + def message_process(self, cr, uid, model, message, custom_values=None, + save_original=False, strip_attachments=False, + context=None): + """Process an incoming RFC2822 email message related to the + given thread model, relying on ``mail.message.parse()`` + for the parsing operation, and then calling ``message_new`` + (if the thread record did not exist) or ``message_update`` + (if it did), then calling ``message_forward`` to automatically + notify other people that should receive this message. + + :param string model: the thread model for which a new message + must be processed + :param message: source of the RFC2822 mail + :type message: string or xmlrpclib.Binary + :type dict custom_values: optional dictionary of field values + to pass to ``message_new`` if a new + record needs to be created. Ignored + if the thread record already exists. + :param bool save_original: whether to keep a copy of the original + email source attached to the message after it is imported. + :param bool strip_attachments: whether to strip all attachments + before processing the message, in order to save some space. + """ + # extract message bytes - we are forced to pass the message as binary because + # we don't know its encoding until we parse its headers and hence can't + # convert it to utf-8 for transport between the mailgate script and here. + if isinstance(message, xmlrpclib.Binary): + message = str(message.data) + + model_pool = self.pool.get(model) + if self._name != model: + if context is None: context = {} + context.update({'thread_model': model}) + + mail_message = self.pool.get('mail.message') + res_id = False + + # Parse Message + # Warning: message_from_string doesn't always work correctly on unicode, + # we must use utf-8 strings here :-( + if isinstance(message, unicode): + message = message.encode('utf-8') + msg_txt = email.message_from_string(message) + msg = mail_message.parse_message(msg_txt, save_original=save_original) + + if strip_attachments and 'attachments' in msg: + del msg['attachments'] + + # Create New Record into particular model + def create_record(msg): + if hasattr(model_pool, 'message_new'): + return model_pool.message_new(cr, uid, msg, + custom_values, + context=context) + res_id = False + if msg.get('references') or msg.get('in-reply-to'): + references = msg.get('references') or msg.get('in-reply-to') + if '\r\n' in references: + references = references.split('\r\n') + else: + references = references.split(' ') + for ref in references: + ref = ref.strip() + res_id = tools.reference_re.search(ref) + if res_id: + res_id = res_id.group(1) + else: + res_id = tools.res_re.search(msg['subject']) + if res_id: + res_id = res_id.group(1) + if res_id: + res_id = int(res_id) + if model_pool.exists(cr, uid, res_id): + if hasattr(model_pool, 'message_update'): + model_pool.message_update(cr, uid, [res_id], msg, {}, context=context) + else: + # referenced thread was not found, we'll have to create a new one + res_id = False + if not res_id: + res_id = create_record(msg) + #To forward the email to other followers + self.message_forward(cr, uid, model, [res_id], msg_txt, context=context) + return res_id + + # for backwards-compatibility with old scripts + process_email = message_process + + def message_forward(self, cr, uid, model, thread_ids, msg, email_error=False, context=None): + """Sends an email to all people following the given threads. + The emails are forwarded immediately, not queued for sending, + and not archived. + + :param str model: thread model + :param list thread_ids: ids of the thread records + :param msg: email.message.Message object to forward + :param email_error: optional email address to notify in case + of any delivery error during the forward. + :return: True + """ + model_pool = self.pool.get(model) + smtp_server_obj = self.pool.get('ir.mail_server') + mail_message = self.pool.get('mail.message') + for res in model_pool.browse(cr, uid, thread_ids, context=context): + if hasattr(model_pool, 'message_thread_followers'): + followers = model_pool.message_thread_followers(cr, uid, [res.id])[res.id] + else: + followers = self.message_thread_followers(cr, uid, [res.id])[res.id] + message_followers_emails = to_email(','.join(filter(None, followers))) + message_recipients = to_email(','.join(filter(None, + [decode(msg['from']), + decode(msg['to']), + decode(msg['cc'])]))) + forward_to = [i for i in message_followers_emails if (i and (i not in message_recipients))] + if forward_to: + # TODO: we need an interface for this for all types of objects, not just leads + if hasattr(res, 'section_id'): + del msg['reply-to'] + msg['reply-to'] = res.section_id.reply_to + + smtp_from, = to_email(msg['from']) + msg['from'] = smtp_from + msg['to'] = ", ".join(forward_to) + msg['message-id'] = tools.generate_tracking_message_id(res.id) + if not smtp_server_obj.send_email(cr, uid, msg) and email_error: + subj = msg['subject'] + del msg['subject'], msg['to'], msg['cc'], msg['bcc'] + msg['subject'] = _('[OpenERP-Forward-Failed] %s') % subj + msg['to'] = email_error + smtp_server_obj.send_email(cr, uid, msg) + return True + + def message_partner_by_email(self, cr, uid, email, context=None): + """Attempts to return the id of a partner address matching + the given ``email``, and the corresponding partner id. + Can be used by classes using the ``mail.thread`` mixin + to lookup the partner and use it in their implementation + of ``message_new`` to link the new record with a + corresponding partner. + The keys used in the returned dict are meant to map + to usual names for relationships towards a partner + and one of its addresses. + + :param email: email address for which a partner + should be searched for. + :rtype: dict + :return: a map of the following form:: + + { 'partner_address_id': id or False, + 'partner_id': pid or False } + """ + address_pool = self.pool.get('res.partner.address') + res = { + 'partner_address_id': False, + 'partner_id': False + } + if email: + email = to_email(email)[0] + address_ids = address_pool.search(cr, uid, [('email', '=', email)]) + if address_ids: + address = address_pool.browse(cr, uid, address_ids[0]) + res['partner_address_id'] = address_ids[0] + res['partner_id'] = address.partner_id.id + return res + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..bd10a72faf293161fa413cb180ea13ca54600114 --- /dev/null +++ b/addons/mail/mail_thread_view.xml @@ -0,0 +1,72 @@ +<?xml version="1.0"?> +<openerp> + <data> + <record model="ir.ui.view" id="view_mailgate_thread_form"> + <field name="name">mail.thread.form</field> + <field name="model">mail.thread</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Email Thread"> + <separator string="Communication History" colspan="4"/> + <field name="message_ids" nolabel="1" colspan="4" mode="tree,form"> + <tree string="Communication History"> + <field name="display_text"/> + </tree> + <form string="Message"> + <field name="subject" widget="char"/> + <field name="date"/> + <field name="user_id"/> + <notebook colspan="4"> + <page string="Email Details"> + <group col="4" colspan="4"> + <separator string="Email Details" colspan="4"/> + <field name="email_from"/> + <field name="email_to"/> + <field name="email_cc"/> + <field name="email_bcc"/> + </group> + </page> + <page string="Attachments"> + <field name="attachment_ids" nolabel="1" colspan="4" /> + </page> + </notebook> + </form> + </field> + </form> + </field> + </record> + + <record model="ir.ui.view" id="view_mailgate_thread_tree"> + <field name="name">mail.thread.tree</field> + <field name="model">mail.thread</field> + <field name="type">tree</field> + <field name="arch" type="xml"> + <tree string="Email Threads"> + <field name="message_ids" /> + </tree> + </field> + </record> + + <!-- Emails thread action --> + <record model="ir.actions.act_window" id="action_view_mailgate_thread"> + <field name="name">Email Threads</field> + <field name="res_model">mail.thread</field> + <field name="view_mode">tree,form</field> + <field name="view_type">form</field> + <field name="view_id" ref="view_mailgate_thread_tree"/> + </record> + <record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view1"> + <field name="sequence" eval="1"/> + <field name="view_mode">tree</field> + <field name="view_id" ref="view_mailgate_thread_tree"/> + <field name="act_window_id" ref="action_view_mailgate_thread"/> + </record> + <record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view2"> + <field name="sequence" eval="2"/> + <field name="view_mode">form</field> + <field name="view_id" ref="view_mailgate_thread_form"/> + <field name="act_window_id" ref="action_view_mailgate_thread"/> + </record> + + </data> +</openerp> diff --git a/addons/mail_gateway/res_partner.py b/addons/mail/res_partner.py similarity index 86% rename from addons/mail_gateway/res_partner.py rename to addons/mail/res_partner.py index c6f41ff0414dc316a12021d0fcdf7512affd3ff9..637922ff75c83578c11605599537df15e9f8ff48 100644 --- a/addons/mail_gateway/res_partner.py +++ b/addons/mail/res_partner.py @@ -19,17 +19,16 @@ # ############################################################################## -from osv import fields,osv +from osv import osv +from osv import fields class res_partner(osv.osv): """ Inherits partner and adds CRM information in the partner form """ _inherit = 'res.partner' _columns = { - 'emails': fields.one2many('mailgate.message', 'partner_id',\ - 'Emails', readonly=True, domain=[('history','=',True)]), + 'emails': fields.one2many('mail.message', 'partner_id', 'Emails', readonly=True, domain=[('email_from','!=',False)]), } res_partner() - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/res_partner_view.xml b/addons/mail/res_partner_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..21da7d735b7962829ef36f66914d76e301e04ae7 --- /dev/null +++ b/addons/mail/res_partner_view.xml @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<openerp> + <data> + + <!-- Partners inherited form --> + <record id="base.view_crm_partner_info_History" model="ir.ui.view"> + <field name="name">res.partner.crm.history.inherit1</field> + <field name="model">res.partner</field> + <field name="type">form</field> + <field name="inherit_id" ref="base.view_partner_form"/> + <field name="arch" type="xml"> + <xpath expr="/form/notebook/page[@string='History']" position="attributes"> + <attribute name="invisible">False</attribute> + </xpath> + </field> + </record> + <record id="view_emails_partner_info_form" model="ir.ui.view"> + <field name="name">res.partner.emails.info.inherit</field> + <field name="model">res.partner</field> + <field name="type">form</field> + <field name="inherit_id" ref="base.view_partner_form"/> + <field name="arch" type="xml"> + <page string="History" position="inside"> + <field name="emails" colspan="4" nolabel="1"/> + </page> + </field> + </record> + + </data> +</openerp> \ No newline at end of file diff --git a/addons/mail_gateway/scripts/__init__.py b/addons/mail/scripts/__init__.py similarity index 91% rename from addons/mail_gateway/scripts/__init__.py rename to addons/mail/scripts/__init__.py index 9d7f6bf56c9e233a82961c72199912e58cbd5dc6..3b6a8c7e4b09b278586f0915ea05c81c85527e3f 100644 --- a/addons/mail_gateway/scripts/__init__.py +++ b/addons/mail/scripts/__init__.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). +# Copyright (C) 2009-2010 OpenERP SA (<http://www.openerp.com>). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -19,6 +19,4 @@ # ############################################################################## -import openerp_mailgate - - +import openerp_mailgate \ No newline at end of file diff --git a/addons/mail_gateway/scripts/openerp_mailgate/__init__.py b/addons/mail/scripts/openerp_mailgate/__init__.py similarity index 93% rename from addons/mail_gateway/scripts/openerp_mailgate/__init__.py rename to addons/mail/scripts/openerp_mailgate/__init__.py index 4f7fae0a1908b419ceffa63fa0d2f12f3a4ebb62..de2e2bf390d887bf17056c7bbf5bf5cfb5f7fde6 100644 --- a/addons/mail_gateway/scripts/openerp_mailgate/__init__.py +++ b/addons/mail/scripts/openerp_mailgate/__init__.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). +# Copyright (C) 2009-2010 OpenERP SA (<http://www.openerp.com>). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py b/addons/mail/scripts/openerp_mailgate/openerp_mailgate.py similarity index 86% rename from addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py rename to addons/mail/scripts/openerp_mailgate/openerp_mailgate.py index bcff408ef468a3ef69ed09ca72d296a4e3128788..68ae20d35e38a28ee97b6a3144be7ba82389c413 100755 --- a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py +++ b/addons/mail/scripts/openerp_mailgate/openerp_mailgate.py @@ -46,13 +46,14 @@ class DefaultConfig(object): OPENERP_PORT = 8069 OPENERP_DEFAULT_DATABASE = 'openerp' MAIL_ERROR = 'error@example.com' - MAIL_SERVER = 'localhost' - MAIL_ADMINS = ('admin@example.com',) + MAIL_SERVER = 'smtp.example.com' + MAIL_SERVER_PORT = 25 + MAIL_ADMINS = ('info@example.com',) config = DefaultConfig() -def send_mail(_from_, to_, subject, text, files=None, server=config.MAIL_SERVER): +def send_mail(_from_, to_, subject, text, files=None, server=config.MAIL_SERVER, port=config.MAIL_SERVER_PORT): assert isinstance(to_, (list, tuple)) if files is None: @@ -74,7 +75,7 @@ def send_mail(_from_, to_, subject, text, files=None, server=config.MAIL_SERVER) % file_name) msg.attach(part) - smtp = smtplib.SMTP(server) + smtp = smtplib.SMTP(server, port=port) smtp.sendmail(_from_, to_, msg.as_string() ) smtp.close() @@ -104,16 +105,15 @@ class EmailParser(object): self.email_default = email_default - def parse(self, message, custom_values=None): - if custom_values is None: - custom_values = {} + def parse(self, message, custom_values=None, save_original=None): # pass message as bytes because we don't know its encoding until we parse its headers # and hence can't convert it to utf-8 for transport - self.rpc('email.server.tools', - 'process_email', - self.model, - xmlrpclib.Binary(message), - custom_values) + res_id = self.rpc('mail.thread', + 'message_process', + self.model, + xmlrpclib.Binary(message), + custom_values or {}, + save_original or False) def configure_parser(): parser = optparse.OptionParser(usage='usage: %prog [options]', version='%prog v1.1') @@ -145,6 +145,10 @@ def configure_parser(): parser.add_option("--custom-values", dest="custom_values", help="Add Custom Values to the object", default=None) + parser.add_option("-s", dest="save_original", + action="store_true", + help="Attach a copy of original email to the message entry", + default=False) return parser @@ -171,10 +175,11 @@ def main(): try: custom_values = dict(eval(options.custom_values or {} )) except: - pass + import traceback + traceback.print_exc() try: - email_parser.parse(msg_txt, custom_values) + email_parser.parse(msg_txt, custom_values, options.save_original or False) except Exception: msg = '\n'.join([ 'parameters', @@ -191,6 +196,7 @@ def main(): config.MAIL_ADMINS, subject, msg, files=[('message.txt', msg_txt)] ) + sys.stderr.write("Failed to deliver email to OpenERP Server, sending error notification to %s\n" % config.MAIL_ADMINS) if __name__ == '__main__': main() diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv new file mode 100644 index 0000000000000000000000000000000000000000..b508cd68d83f6ac657d18ad8efcdf7fda4041030 --- /dev/null +++ b/addons/mail/security/ir.model.access.csv @@ -0,0 +1,3 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_mail_message","mail.message","model_mail_message",,1,0,0,0 +"access_mail_thread","mail.thread","model_mail_thread",,1,0,0,0 diff --git a/addons/mail/wizard/__init__.py b/addons/mail/wizard/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..88b9581f68e96df6c3b3673747074b3d7c01d79e --- /dev/null +++ b/addons/mail/wizard/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +import mail_compose_message + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py new file mode 100644 index 0000000000000000000000000000000000000000..0bfaf06890aaa8d3eb8eb2812fd40b9b855f9a8c --- /dev/null +++ b/addons/mail/wizard/mail_compose_message.py @@ -0,0 +1,272 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +import re + +import tools +from mail.mail_message import to_email +from osv import osv +from osv import fields +from tools.safe_eval import safe_eval as eval +from tools.safe_eval import literal_eval +from tools.translate import _ + +# main mako-like expression pattern +EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') + +class mail_compose_message(osv.osv_memory): + """Generic E-mail composition wizard. This wizard is meant to be inherited + at model and view level to provide specific wizard features. + + The behavior of the wizard can be modified through the use of context + parameters, among which are: + + * mail.compose.message.mode: if set to 'reply', the wizard is in + reply mode and pre-populated with the original quote. + If set to 'mass_mail', the wizard is in mass mailing + where the mail details can contain template placeholders + that will be merged with actual data before being sent + to each recipient. Recipients will be derived from the + records determined via ``context['active_model']`` and + ``context['active_ids']``. + * active_model: model name of the document to which the mail being + composed is related + * active_id: id of the document to which the mail being composed is + related, or id of the message to which user is replying, + in case ``mail.compose.message.mode == 'reply'`` + * active_ids: ids of the documents to which the mail being composed is + related, in case ``mail.compose.message.mode == 'mass_mail'``. + """ + _name = 'mail.compose.message' + _inherit = 'mail.message.common' + _description = 'E-mail composition wizard' + + def default_get(self, cr, uid, fields, context=None): + """Overridden to provide specific defaults depending on the context + parameters. + + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + """ + if context is None: + context = {} + result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) + vals = {} + reply_mode = context.get('mail.compose.message.mode') == 'reply' + if (not reply_mode) and context.get('active_model') and context.get('active_id'): + # normal mode when sending an email related to any document, as specified by + # active_model and active_id in context + vals = self.get_value(cr, uid, context.get('active_model'), context.get('active_id'), context) + elif reply_mode and context.get('active_id'): + # reply mode, consider active_id is the ID of a mail.message to which we're + # replying + vals = self.get_message_data(cr, uid, int(context['active_id']), context) + else: + # default mode + result['model'] = context.get('active_model', False) + if vals: + for field in fields: + result.update({field : vals.get(field, False)}) + + # link to model and record if not done yet + if not result.get('model') or not result.get('res_id'): + active_model = context.get('active_model') + res_id = context.get('active_id') + if active_model and active_model not in (self._name, 'mail.message'): + result['model'] = active_model + if res_id: + result['res_id'] = res_id + + # Try to provide default email_from if not specified yet + if not result.get('email_from'): + current_user = self.pool.get('res.users').browse(cr, uid, uid, context) + result['email_from'] = current_user.user_email or False + return result + + _columns = { + 'attachment_ids': fields.many2many('ir.attachment','email_message_send_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'), + 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"), + 'filter_id': fields.many2one('ir.filters', 'Filters'), + } + + def get_value(self, cr, uid, model, res_id, context=None): + """Returns a defaults-like dict with initial values for the composition + wizard when sending an email related to the document record identified + by ``model`` and ``res_id``. + + The default implementation returns an empty dictionary, and is meant + to be overridden by subclasses. + + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + """ + return {} + + def get_message_data(self, cr, uid, message_id, context=None): + """Returns a defaults-like dict with initial values for the composition + wizard when replying to the given message (e.g. including the quote + of the initial message, and the correct recipient). + Should not be called unless ``context['mail.compose.message.mode'] == 'reply'``. + + :param int message_id: id of the mail.message to which the user + is replying. + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + When calling this method, the ``'mail'`` value + in the context should be ``'reply'``. + """ + if context is None: + context = {} + result = {} + mail_message = self.pool.get('mail.message') + if message_id: + message_data = mail_message.browse(cr, uid, message_id, context) + subject = tools.ustr(message_data.subject or '') + # we use the plain text version of the original mail, by default, + # as it is easier to quote than the HTML version. + # XXX TODO: make it possible to switch to HTML on the fly + current_user = self.pool.get('res.users').browse(cr, uid, uid, context) + body = message_data.body_text or current_user.signature + if context.get('mail.compose.message.mode') == 'reply': + sent_date = _('On %(date)s, ') % {'date': message_data.date} if message_data.date else '' + sender = _('%(sender_name)s wrote:') % {'sender_name': tools.ustr(message_data.email_from or _('You'))} + quoted_body = '> %s' % tools.ustr(body.replace('\n', "\n> ") or '') + body = '\n'.join(["\n", (sent_date + sender), quoted_body]) + body += "\n" + current_user.signature + re_prefix = _("Re:") + if not (subject.startswith('Re:') or subject.startswith(re_prefix)): + subject = "%s %s" % (re_prefix, subject) + result.update({ + 'subtype' : 'plain', # default to the text version due to quoting + 'body_text' : body, + 'subject' : subject, + 'attachment_ids' : [], + 'model' : message_data.model or False, + 'res_id' : message_data.res_id or False, + 'email_from' : current_user.user_email or message_data.email_to or False, + 'email_to' : message_data.reply_to or message_data.email_from or False, + 'email_cc' : message_data.email_cc or False, + 'user_id' : uid, + + # pass msg-id and references of mail we're replying to, to construct the + # new ones later when sending + 'message_id' : message_data.message_id or False, + 'references' : message_data.references and tools.ustr(message_data.references) or False, + }) + return result + + def send_mail(self, cr, uid, ids, context=None): + '''Process the wizard contents and proceed with sending the corresponding + email(s), rendering any template patterns on the fly if needed. + If the wizard is in mass-mail mode (context['mail.compose.message.mode'] is + set to ``'mass_mail'``), the resulting email(s) are scheduled for being + sent the next time the mail.message scheduler runs, or the next time + ``mail.message.process_email_queue`` is called. + Otherwise the new message is sent immediately. + + :param dict context: several context values will modify the behavior + of the wizard, cfr. the class description. + ''' + if context is None: + context = {} + mail_message = self.pool.get('mail.message') + for mail in self.browse(cr, uid, ids, context=context): + attachment = {} + for attach in mail.attachment_ids: + attachment[attach.datas_fname] = attach.datas + references = None + headers = {} + + body = mail.body_html if mail.subtype == 'html' else mail.body_text + + # Reply Email + if context.get('mail.compose.message.mode') == 'reply' and mail.message_id: + references = (mail.references or '') + " " + mail.message_id + headers['In-Reply-To'] = mail.message_id + + if context.get('mail.compose.message.mode') == 'mass_mail': + # Mass mailing: must render the template patterns + if context.get('active_ids') and context.get('active_model'): + active_ids = context['active_ids'] + active_model = context['active_model'] + else: + active_model = mail.model + active_model_pool = self.pool.get(active_model) + active_ids = active_model_pool.search(cr, uid, literal_eval(mail.filter_id.domain), context=literal_eval(mail.filter_id.context)) + + for active_id in active_ids: + subject = self.render_template(cr, uid, mail.subject, active_model, active_id) + rendered_body = self.render_template(cr, uid, body, active_model, active_id) + email_from = self.render_template(cr, uid, mail.email_from, active_model, active_id) + email_to = self.render_template(cr, uid, mail.email_to, active_model, active_id) + email_cc = self.render_template(cr, uid, mail.email_cc, active_model, active_id) + email_bcc = self.render_template(cr, uid, mail.email_bcc, active_model, active_id) + reply_to = self.render_template(cr, uid, mail.reply_to, active_model, active_id) + + # in mass-mailing mode we only schedule the mail for sending, it will be + # processed as soon as the mail scheduler runs. + mail_message.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, rendered_body, + model=mail.model, email_cc=to_email(email_cc), email_bcc=to_email(email_bcc), reply_to=reply_to, + attachments=attachment, references=references, res_id=int(mail.res_id), + subtype=mail.subtype, headers=headers, context=context) + else: + # normal mode - no mass-mailing + msg_id = mail_message.schedule_with_attach(cr, uid, mail.email_from, to_email(mail.email_to), mail.subject, body, + model=mail.model, email_cc=to_email(mail.email_cc), email_bcc=to_email(mail.email_bcc), reply_to=mail.reply_to, + attachments=attachment, references=references, res_id=int(mail.res_id), + subtype=mail.subtype, headers=headers, context=context) + # in normal mode, we send the email immediately, as the user expects us to (delay should be sufficiently small) + mail_message.send(cr, uid, [msg_id], context) + + return {'type': 'ir.actions.act_window_close'} + + def render_template(self, cr, uid, template, model, res_id, context=None): + """Render the given template text, replace mako-like expressions ``${expr}`` + with the result of evaluating these expressions with an evaluation context + containing: + + * ``user``: browse_record of the current user + * ``object``: browse_record of the document record this mail is + related to + * ``context``: the context passed to the mail composition wizard + + :param str template: the template text to render + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. + """ + if context is None: + context = {} + def merge(match): + exp = str(match.group()[2:-1]).strip() + result = eval(exp, + { + 'user' : self.pool.get('res.users').browse(cr, uid, uid, context=context), + 'object' : self.pool.get(model).browse(cr, uid, res_id, context=context), + 'context': dict(context), # copy context to prevent side-effects of eval + }) + if result in (None, False): + return "" + return tools.ustr(result) + return template and EXPRESSION_PATTERN.sub(merge, template) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..d559d679c8b9b0af3cfef0568fc2e0f075855cb6 --- /dev/null +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record model="ir.ui.view" id="email_compose_message_wizard_form"> + <field name="name">mail.compose.message.form</field> + <field name="model">mail.compose.message</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Compose Email"> + <group col="6" colspan="4"> + <field name="model" invisible="1"/> + <field name="res_id" invisible="1"/> + <field name='filter_id' invisible="context.get('active_model',False)"/> + <field name="email_from" colspan="4" required="1"/> + <field name="email_to" colspan="4" required="1"/> + <field name="email_cc" colspan="4"/> + <field name="email_bcc" colspan="4"/> + <field name="reply_to" colspan="4"/> + <field name="subject" colspan="4" widget="char" size="512"/> + <field name="references" invisible="1"/> + <field name="message_id" invisible="1"/> + </group> + <notebook colspan="4"> + <page string="Body"> + <field name="body_text" colspan="4" nolabel="1" height="300" width="300"/> + </page> + <page string="Attachments"> + <field name="attachment_ids" colspan="4" nolabel="1"/> + </page> + </notebook> + <group col="4" colspan="4"> + <label string="" name="placeholder" colspan="1"/> + <button icon="gtk-close" special="cancel" string="Cancel"/> + <button icon="gtk-ok" name="send_mail" string="Send" type="object"/> + </group> + </form> + </field> + </record> + + <record id="action_email_compose_message_wizard" model="ir.actions.act_window"> + <field name="name">Compose Email</field> + <field name="res_model">mail.compose.message</field> + <field name="src_model">mail.compose.message</field> + <field name="type">ir.actions.act_window</field> + <field name="view_type">form</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> + + <!-- Replace the default mass-mailing wizard in base with the composition wizard --> + <act_window name="Mass Mailing" + res_model="mail.compose.message" + src_model="res.partner" + view_mode="form" + target="new" + key2="client_action_multi" + id="base.action_partner_mass_mail" + context="{'mail.compose.message.mode':'mass_mail'}"/> + </data> +</openerp> diff --git a/addons/mail_gateway/mail_gateway.py b/addons/mail_gateway/mail_gateway.py deleted file mode 100644 index 3afc3fb6ba0def2e61d526ac760cbe0dc4ad5ca0..0000000000000000000000000000000000000000 --- a/addons/mail_gateway/mail_gateway.py +++ /dev/null @@ -1,603 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -############################################################################## - -from osv import osv, fields -import time -import tools -import binascii -import email -from email.header import decode_header -from email.utils import parsedate -import base64 -import re -from tools.translate import _ -import logging -import xmlrpclib - -_logger = logging.getLogger('mailgate') - -class mailgate_thread(osv.osv): - ''' - Mailgateway Thread - ''' - _name = 'mailgate.thread' - _description = 'Mailgateway Thread' - - _columns = { - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', readonly=True), - } - - def copy(self, cr, uid, id, default=None, context=None): - """ - Overrides orm copy method. - @param self: the object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param id: Id of mailgate thread - @param default: Dictionary of default values for copy. - @param context: A standard dictionary for contextual values - """ - if default is None: - default = {} - - default.update({ - 'message_ids': [], - 'date_closed': False, - 'date_open': False - }) - return super(mailgate_thread, self).copy(cr, uid, id, default, context=context) - - def message_new(self, cr, uid, msg, context): - raise Exception, _('Method is not implemented') - - def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context=None): - raise Exception, _('Method is not implemented') - - def message_followers(self, cr, uid, ids, context=None): - """ Get a list of emails of the people following this thread - """ - res = {} - if isinstance(ids, (str, int, long)): - ids = [long(ids)] - for thread in self.browse(cr, uid, ids, context=context): - l=[] - for message in thread.message_ids: - l.append((message.user_id and message.user_id.user_email) or '') - l.append(message.email_from or '') - l.append(message.email_cc or '') - res[thread.id] = l - return res - - def msg_send(self, cr, uid, id, *args, **argv): - raise Exception, _('Method is not implemented') - - def history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, \ - email_from=False, message_id=False, references=None, attach=None, email_cc=None, \ - email_bcc=None, email_date=None, context=None): - """ - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param cases: a browse record list - @param keyword: Subject of the history item - @param history: Value True/False, If True it makes entry in case History otherwise in Case Log - @param email: Email-To / Recipient address - @param email_from: Email From / Sender address if any - @param email_cc: Comma-Separated list of Carbon Copy Emails To addresse if any - @param email_bcc: Comma-Separated list of Blind Carbon Copy Emails To addresses if any - @param email_date: Email Date string if different from now, in server Timezone - @param details: Description, Details of case history if any - @param atach: Attachment sent in email - @param context: A standard dictionary for contextual values""" - if context is None: - context = {} - if attach is None: - attach = [] - - if email_date: - edate = parsedate(email_date) - if edate is not None: - email_date = time.strftime('%Y-%m-%d %H:%M:%S', edate) - - # The mailgate sends the ids of the cases and not the object list - - if all(isinstance(case_id, (int, long)) for case_id in cases): - cases = self.browse(cr, uid, cases, context=context) - - att_obj = self.pool.get('ir.attachment') - obj = self.pool.get('mailgate.message') - - for case in cases: - attachments = [] - for att in attach: - att_ids = att_obj.search(cr, uid, [('name','=',att[0]), ('res_id', '=', case.id)]) - if att_ids: - attachments.append(att_ids[0]) - else: - attachments.append(att_obj.create(cr, uid, {'res_model':case._name,'res_id':case.id, 'name': att[0], 'datas': base64.encodestring(att[1])})) - - partner_id = hasattr(case, 'partner_id') and (case.partner_id and case.partner_id.id or False) or False - if not partner_id and case._name == 'res.partner': - partner_id = case.id - data = { - 'name': keyword, - 'user_id': uid, - 'model' : case._name, - 'partner_id': partner_id, - 'res_id': case.id, - 'date': time.strftime('%Y-%m-%d %H:%M:%S'), - 'message_id': message_id, - 'description': details, - 'attachment_ids': [(6, 0, attachments)] - } - - if history: - for param in (email, email_cc, email_bcc): - if isinstance(param, list): - param = ", ".join(param) - - data = { - 'name': subject or _('History'), - 'history': True, - 'user_id': uid, - 'model' : case._name, - 'res_id': case.id, - 'date': email_date or time.strftime('%Y-%m-%d %H:%M:%S'), - 'description': details or (hasattr(case, 'description') and case.description or False), - 'email_to': email, - 'email_from': email_from or \ - (hasattr(case, 'user_id') and case.user_id and case.user_id.user_email), - 'email_cc': email_cc, - 'email_bcc': email_bcc, - 'partner_id': partner_id, - 'references': references, - 'message_id': message_id, - 'attachment_ids': [(6, 0, attachments)] - } - - obj.create(cr, uid, data, context=context) - return True -mailgate_thread() - -def format_date_tz(date, tz=None): - if not date: - return 'n/a' - format = tools.DEFAULT_SERVER_DATETIME_FORMAT - return tools.server_to_local_timestamp(date, format, format, tz) - -class mailgate_message(osv.osv): - ''' - Mailgateway Message - ''' - def open_document(self, cr, uid, ids, context=None): - """ To Open Document - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param ids: the ID of messages - @param context: A standard dictionary - """ - action_data = False - if ids: - message_id = ids[0] - mailgate_data = self.browse(cr, uid, message_id, context=context) - model = mailgate_data.model - res_id = mailgate_data.res_id - - action_pool = self.pool.get('ir.actions.act_window') - action_ids = action_pool.search(cr, uid, [('res_model', '=', model)]) - if action_ids: - action_data = action_pool.read(cr, uid, action_ids[0], context=context) - action_data.update({ - 'domain' : "[('id','=',%d)]"%(res_id), - 'nodestroy': True, - 'context': {} - }) - return action_data - - def open_attachment(self, cr, uid, ids, context=None): - """ To Open attachments - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param ids: the ID of messages - @param context: A standard dictionary - """ - action_data = False - action_pool = self.pool.get('ir.actions.act_window') - message_pool = self.browse(cr ,uid, ids, context=context)[0] - att_ids = [x.id for x in message_pool.attachment_ids] - action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')]) - if action_ids: - action_data = action_pool.read(cr, uid, action_ids[0], context=context) - action_data.update({ - 'domain': [('id','in',att_ids)], - 'nodestroy': True - }) - return action_data - - def truncate_data(self, cr, uid, data, context=None): - data_list = data and data.split('\n') or [] - if len(data_list) > 3: - res = '\n\t'.join(data_list[:3]) + '...' - else: - res = '\n\t'.join(data_list) - return res - - def _get_display_text(self, cr, uid, ids, name, arg, context=None): - if context is None: - context = {} - tz = context.get('tz') - result = {} - for message in self.browse(cr, uid, ids, context=context): - msg_txt = '' - if message.history: - msg_txt += _('%s wrote on %s: \n Subject: %s \n\t') % (message.email_from or '/', format_date_tz(message.date, tz), message.name) - if message.description: - msg_txt += self.truncate_data(cr, uid, message.description, context=context) - else: - msg_txt = _('%s on %s:\n\t') % (message.user_id.name or '/', format_date_tz(message.date, tz)) - msg_txt += message.name - result[message.id] = msg_txt - return result - - _name = 'mailgate.message' - _description = 'Mailgateway Message' - _order = 'date desc' - _columns = { - 'name':fields.text('Subject', readonly=True), - 'model': fields.char('Object Name', size=128, select=1, readonly=True), - 'res_id': fields.integer('Resource ID', select=1, readonly=True), - 'ref_id': fields.char('Reference Id', size=256, readonly=True, help="Message Id in Email Server.", select=True), - 'date': fields.datetime('Date', readonly=True), - 'history': fields.boolean('Is History?', readonly=True), - 'user_id': fields.many2one('res.users', 'User Responsible', readonly=True), - 'message': fields.text('Message', readonly=True), - 'email_from': fields.char('From', size=128, help="Email From", readonly=True), - 'email_to': fields.char('To', help="Email Recipients", size=256, readonly=True), - 'email_cc': fields.char('Cc', help="Carbon Copy Email Recipients", size=256, readonly=True), - 'email_bcc': fields.char('Bcc', help='Blind Carbon Copy Email Recipients', size=256, readonly=True), - 'message_id': fields.char('Message Id', size=1024, readonly=True, help="Message Id on Email.", select=True), - 'references': fields.text('References', readonly=True, help="References emails."), - 'description': fields.text('Description', readonly=True), - 'partner_id': fields.many2one('res.partner', 'Partner', required=False), - 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments', readonly=True), - 'display_text': fields.function(_get_display_text, type='text', size="512", string='Display Text'), - } - - def init(self, cr): - cr.execute("""SELECT indexname - FROM pg_indexes - WHERE indexname = 'mailgate_message_res_id_model_idx'""") - if not cr.fetchone(): - cr.execute("""CREATE INDEX mailgate_message_res_id_model_idx - ON mailgate_message (model, res_id)""") - -mailgate_message() - -class mailgate_tool(osv.osv_memory): - - _name = 'email.server.tools' - _description = "Email Server Tools" - - def _decode_header(self, text): - """Returns unicode() string conversion of the the given encoded smtp header""" - if text: - text = decode_header(text.replace('\r', '')) - return ''.join([tools.ustr(x[0], x[1]) for x in text]) - - def to_email(self,text): - return re.findall(r'([^ ,<@]+@[^> ,]+)',text) - - def history(self, cr, uid, model, res_ids, msg, attach, context=None): - """This function creates history for mails fetched - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param model: OpenObject Model - @param res_ids: Ids of the record of OpenObject model created - @param msg: Email details - @param attach: Email attachments - """ - if isinstance(res_ids, (int, long)): - res_ids = [res_ids] - - msg_pool = self.pool.get('mailgate.message') - for res_id in res_ids: - case = self.pool.get(model).browse(cr, uid, res_id, context=context) - partner_id = hasattr(case, 'partner_id') and (case.partner_id and case.partner_id.id or False) or False - if not partner_id and model == 'res.partner': - partner_id = res_id - msg_data = { - 'name': msg.get('subject', 'No subject'), - 'date': msg.get('date'), - 'description': msg.get('body', msg.get('from')), - 'history': True, - 'partner_id': partner_id, - 'model': model, - 'email_cc': msg.get('cc'), - 'email_from': msg.get('from'), - 'email_to': msg.get('to'), - 'message_id': msg.get('message-id'), - 'references': msg.get('references') or msg.get('in-reply-to'), - 'res_id': res_id, - 'user_id': uid, - 'attachment_ids': [(6, 0, attach)] - } - msg_pool.create(cr, uid, msg_data, context=context) - return True - - def email_forward(self, cr, uid, model, res_ids, msg, email_error=False, context=None): - """Sends an email to all people following the thread - @param res_id: Id of the record of OpenObject model created from the email message - @param msg: email.message.Message to forward - @param email_error: Default Email address in case of any Problem - """ - model_pool = self.pool.get(model) - - for res in model_pool.browse(cr, uid, res_ids, context=context): - message_followers = model_pool.message_followers(cr, uid, [res.id])[res.id] - message_followers_emails = self.to_email(','.join(filter(None, message_followers))) - message_recipients = self.to_email(','.join(filter(None, - [self._decode_header(msg['from']), - self._decode_header(msg['to']), - self._decode_header(msg['cc'])]))) - message_forward = [i for i in message_followers_emails if (i and (i not in message_recipients))] - - if message_forward: - # TODO: we need an interface for this for all types of objects, not just leads - if hasattr(res, 'section_id'): - del msg['reply-to'] - msg['reply-to'] = res.section_id.reply_to - - smtp_from = self.to_email(msg['from']) - if not tools.misc._email_send(smtp_from, message_forward, msg, openobject_id=res.id) and email_error: - subj = msg['subject'] - del msg['subject'], msg['to'], msg['cc'], msg['bcc'] - msg['subject'] = '[OpenERP-Forward-Failed] %s' % subj - msg['to'] = email_error - tools.misc._email_send(smtp_from, self.to_email(email_error), msg, openobject_id=res.id) - - def process_email(self, cr, uid, model, message, custom_values=None, attach=True, context=None): - """This function Processes email and create record for given OpenERP model - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param model: OpenObject Model - @param message: Email details, passed as a string or an xmlrpclib.Binary - @param attach: Email attachments - @param context: A standard dictionary for contextual values""" - - # extract message bytes, we are forced to pass the message as binary because - # we don't know its encoding until we parse its headers and hence can't - # convert it to utf-8 for transport between the mailgate script and here. - if isinstance(message, xmlrpclib.Binary): - message = str(message.data) - - if context is None: - context = {} - - if custom_values is None or not isinstance(custom_values, dict): - custom_values = {} - - model_pool = self.pool.get(model) - res_id = False - - # Create New Record into particular model - def create_record(msg): - att_ids = [] - if hasattr(model_pool, 'message_new'): - res_id = model_pool.message_new(cr, uid, msg, context=context) - if custom_values: - model_pool.write(cr, uid, [res_id], custom_values, context=context) - else: - data = { - 'name': msg.get('subject'), - 'email_from': msg.get('from'), - 'email_cc': msg.get('cc'), - 'user_id': False, - 'description': msg.get('body'), - 'state' : 'draft', - } - data.update(self.get_partner(cr, uid, msg.get('from'), context=context)) - res_id = model_pool.create(cr, uid, data, context=context) - - if attach: - for attachment in msg.get('attachments', []): - data_attach = { - 'name': attachment, - 'datas': binascii.b2a_base64(str(attachments.get(attachment))), - 'datas_fname': attachment, - 'description': 'Mail attachment', - 'res_model': model, - 'res_id': res_id, - } - att_ids.append(self.pool.get('ir.attachment').create(cr, uid, data_attach)) - - return res_id, att_ids - - # Warning: message_from_string doesn't always work correctly on unicode, - # we must use utf-8 strings here :-( - if isinstance(message, unicode): - message = message.encode('utf-8') - msg_txt = email.message_from_string(message) - message_id = msg_txt.get('message-id', False) - msg = {} - - if not message_id: - # Very unusual situation, be we should be fault-tolerant here - message_id = time.time() - msg_txt['message-id'] = message_id - _logger.info('Message without message-id, generating a random one: %s', message_id) - - fields = msg_txt.keys() - msg['id'] = message_id - msg['message-id'] = message_id - - if 'Subject' in fields: - msg['subject'] = self._decode_header(msg_txt.get('Subject')) - - if 'Content-Type' in fields: - msg['content-type'] = msg_txt.get('Content-Type') - - if 'From' in fields: - msg['from'] = self._decode_header(msg_txt.get('From')) - - if 'Delivered-To' in fields: - msg['to'] = self._decode_header(msg_txt.get('Delivered-To')) - - if 'CC' in fields: - msg['cc'] = self._decode_header(msg_txt.get('CC')) - - if 'Reply-to' in fields: - msg['reply'] = self._decode_header(msg_txt.get('Reply-To')) - - if 'Date' in fields: - msg['date'] = self._decode_header(msg_txt.get('Date')) - - if 'Content-Transfer-Encoding' in fields: - msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') - - if 'References' in fields: - msg['references'] = msg_txt.get('References') - - if 'In-Reply-To' in fields: - msg['in-reply-to'] = msg_txt.get('In-Reply-To') - - if 'X-Priority' in fields: - msg['priority'] = msg_txt.get('X-Priority', '3 (Normal)').split(' ')[0] - - if not msg_txt.is_multipart() or 'text/plain' in msg.get('Content-Type', ''): - encoding = msg_txt.get_content_charset() - body = msg_txt.get_payload(decode=True) - if 'text/html' in msg_txt.get('Content-Type', ''): - body = tools.html2plaintext(body) - msg['body'] = tools.ustr(body, encoding) - - attachments = {} - has_plain_text = False - if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', ''): - body = "" - for part in msg_txt.walk(): - if part.get_content_maintype() == 'multipart': - continue - - encoding = part.get_content_charset() - filename = part.get_filename() - if part.get_content_maintype()=='text': - content = part.get_payload(decode=True) - if filename: - attachments[filename] = content - elif not has_plain_text: - # main content parts should have 'text' maintype - # and no filename. we ignore the html part if - # there is already a plaintext part without filename, - # because presumably these are alternatives. - content = tools.ustr(content, encoding) - if part.get_content_subtype() == 'html': - body = tools.ustr(tools.html2plaintext(content)) - elif part.get_content_subtype() == 'plain': - body = content - has_plain_text = True - elif part.get_content_maintype() in ('application', 'image'): - if filename and attach: - attachments[filename] = part.get_payload(decode=True) - else: - res = part.get_payload(decode=True) - body += tools.ustr(res, encoding) - - msg['body'] = body - msg['attachments'] = attachments - res_ids = [] - attachment_ids = [] - new_res_id = False - if msg.get('references') or msg.get('in-reply-to'): - references = msg.get('references') or msg.get('in-reply-to') - if '\r\n' in references: - references = references.split('\r\n') - else: - references = references.split(' ') - for ref in references: - ref = ref.strip() - res_id = tools.misc.reference_re.search(ref) - if res_id: - res_id = res_id.group(1) - else: - res_id = tools.misc.res_re.search(msg['subject']) - if res_id: - res_id = res_id.group(1) - if res_id: - res_id = int(res_id) - model_pool = self.pool.get(model) - if model_pool.exists(cr, uid, res_id): - res_ids.append(res_id) - if hasattr(model_pool, 'message_update'): - model_pool.message_update(cr, uid, [res_id], {}, msg, context=context) - else: - raise NotImplementedError('model %s does not support updating records, mailgate API method message_update() is missing'%model) - - if not len(res_ids): - new_res_id, attachment_ids = create_record(msg) - res_ids = [new_res_id] - - # Store messages - context.update({'model' : model}) - if hasattr(model_pool, 'history'): - model_pool.history(cr, uid, res_ids, _('receive'), history=True, - subject = msg.get('subject'), - email = msg.get('to'), - details = msg.get('body'), - email_from = msg.get('from'), - email_cc = msg.get('cc'), - message_id = msg.get('message-id'), - references = msg.get('references', False) or msg.get('in-reply-to', False), - attach = attachments.items(), - email_date = msg.get('date'), - context = context) - else: - self.history(cr, uid, model, res_ids, msg, attachment_ids, context=context) - self.email_forward(cr, uid, model, res_ids, msg_txt) - return new_res_id - - def get_partner(self, cr, uid, from_email, context=None): - """This function returns partner Id based on email passed - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks - @param from_email: email address based on that function will search for the correct - """ - address_pool = self.pool.get('res.partner.address') - res = { - 'partner_address_id': False, - 'partner_id': False - } - from_email = self.to_email(from_email)[0] - address_ids = address_pool.search(cr, uid, [('email', 'like', from_email)]) - if address_ids: - address = address_pool.browse(cr, uid, address_ids[0]) - res['partner_address_id'] = address_ids[0] - res['partner_id'] = address.partner_id.id - - return res - -mailgate_tool() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail_gateway/mail_gateway_view.xml b/addons/mail_gateway/mail_gateway_view.xml deleted file mode 100644 index d05bfbc0d9136654ac310084267fd7329a0a2492..0000000000000000000000000000000000000000 --- a/addons/mail_gateway/mail_gateway_view.xml +++ /dev/null @@ -1,220 +0,0 @@ -<?xml version="1.0"?> -<openerp> - <data> - - <menuitem id="base.menu_mail_gateway" name="Emails" - parent="base.menu_base_config" sequence="1" /> - - <record model="ir.ui.view" id="view_mailgate_message_form"> - <field name="name">mailgate.message.form</field> - <field name="model">mailgate.message</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="mailgate message"> - <group colspan="4" col="6"> - <field name="name" string="Subject" required="1" select="1" widget="char" size="512"/> - <field name="date" required="1" select="1"/> - <field name="user_id" string="Owner" select="1"/> - <field name="partner_id" readonly="1" /> - </group> - <notebook colspan="4"> - <page string="Details"> - <group col="2" colspan="2"> - <separator string="Email Followers" colspan="4"/> - <field name="email_from" /> - <field name="email_to" widget="char" size="512"/> - <field name="email_cc" widget="char" size="512"/> - <field name="email_bcc" widget="char" size="512" groups="base.group_extended"/> - </group> - <group col="2" colspan="2"> - <separator string="Message Details" colspan="4"/> - <field name="model" readonly="1"/> - <group col="3" colspan="2"> - <field name="res_id" readonly="1"/> - <button name="open_document" string="Open Document" type="object" icon="gtk-jump-to"/> - </group> - <field name="message_id" /> - <field name="ref_id" /> - </group> - <separator string="Description" colspan="4"/> - <field name="description" nolabel="1" colspan="4" /> - </page> - <page string="Attachments"> - <separator string="Attachments" colspan="4"/> - <field name="attachment_ids" nolabel="1" colspan="4" readonly="1"/> - </page> - </notebook> - </form> - </field> - </record> - - <record model="ir.ui.view" id="view_mailgate_message_tree"> - <field name="name">mailgate.message.tree</field> - <field name="model">mailgate.message</field> - <field name="type">tree</field> - <field name="arch" type="xml"> - <tree string="Emails"> - <field name="date" /> - <field name="name" string="Subject"/> - <field name="email_from" string="From"/> - <field name="user_id" string="Owner"/> - <field name="message_id" string="Message" invisible="1"/> - <field name="partner_id" invisible="1"/> - <button name="open_document" string="Open Document" type="object" icon="gtk-jump-to"/> - <button name="open_attachment" string="Open Attachments" type="object" icon="gtk-jump-to"/> - </tree> - </field> - </record> - - <record model="ir.ui.view" id="view_mailgate_message_search"> - <field name="name">mailgate.message.search</field> - <field name="model">mailgate.message</field> - <field name="type">search</field> - <field name="arch" type="xml"> - <search string="Email Search"> - <field name="name" string="Subject"/> - <field name="date" /> - <field name="user_id" string="Owner"/> - <field name="partner_id" string="Partner Name"/> - <newline/> - <group expand="0" string="Group By..." groups="base.group_extended"> - <filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/> - <filter string="Owner" name="User" icon="terp-personal" context="{'group_by':'user_id'}"/> - <separator orientation="vertical"/> - <filter string="Thread" icon="terp-mail-" domain="[]" context="{'group_by':'message_id'}"/> - <separator orientation="vertical"/> - <filter string="Month" help="Creation Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}"/> - </group> - </search> - </field> - </record> - - - - - <record id="action_view_mail_message" model="ir.actions.act_window"> - <field name="name">Messages</field> - <field name="res_model">mailgate.message</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="search_view_id" ref="view_mailgate_message_search"/> - </record> - - - - - <record model="ir.ui.view" id="view_mailgate_thread_form"> - <field name="name">mailgate.thread.form</field> - <field name="model">mailgate.thread</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Mailgateway Thread"> - <separator string="History" colspan="4"/> - <field name="message_ids" nolabel="1" colspan="4" mode="tree,form"> - <tree string="Mailgateway History"> - <field name="display_text" /> - </tree> - <form string="Mailgate History"> - <field name="name" widget="char" size="512"/> - <field name="date" /> - <field name="user_id" /> - <field name="message_id" /> - <field name="history" /> - <notebook colspan="4"> - <page string="Email Details"> - <group col="4" colspan="4"> - <separator string="Email Details" colspan="4"/> - <field name="email_from" /> - <field name="email_to" widget="char" size="512"/> - <field name="email_cc" widget="char" size="512"/> - <field name="email_bcc" widget="char" size="512"/> - </group> - </page> - <page string="Attachments"> - <separator string="Attachments" colspan="4"/> - <field name="attachment_ids" nolabel="1" colspan="4" /> - </page> - </notebook> - </form> - </field> - </form> - </field> - </record> - - <record model="ir.ui.view" id="view_mailgate_thread_tree"> - <field name="name">mailgate.thread.tree</field> - <field name="model">mailgate.thread</field> - <field name="type">tree</field> - <field name="arch" type="xml"> - <tree string="Mailgateway Thread"> - <field name="message_ids" /> - </tree> - </field> - </record> - - - - - <!-- Emails action--> - <record model="ir.actions.act_window" id="action_view_mailgate_thread"> - <field name="name">Mailgateway Threads</field> - <field name="res_model">mailgate.thread</field> - <field name="view_mode">tree,form</field> - <field name="view_type">form</field> - <field name="view_id" ref="view_mailgate_thread_tree"/> - </record> - - <record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view1"> - <field name="sequence" eval="1"/> - <field name="view_mode">tree</field> - <field name="view_id" ref="view_mailgate_thread_tree"/> - <field name="act_window_id" ref="action_view_mailgate_thread"/> - </record> - <record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view2"> - <field name="sequence" eval="2"/> - <field name="view_mode">form</field> - <field name="view_id" ref="view_mailgate_thread_form"/> - <field name="act_window_id" ref="action_view_mailgate_thread"/> - </record> - - <!-- Emailsaction--> - <record model="ir.actions.act_window" id="action_view_mailgate_message"> - <field name="name">Emails</field> - <field name="res_model">mailgate.message</field> - <field name="view_mode">tree,form</field> - <field name="view_type">form</field> - <field name="domain">[('history', '=', True)]</field> - <field name="view_id" ref="view_mailgate_message_tree"/> - </record> - - <record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view1"> - <field name="sequence" eval="1"/> - <field name="view_mode">tree</field> - <field name="view_id" ref="view_mailgate_thread_tree"/> - <field name="act_window_id" ref="action_view_mailgate_thread"/> - </record> - <record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view2"> - <field name="sequence" eval="2"/> - <field name="view_mode">form</field> - <field name="view_id" ref="view_mailgate_thread_form"/> - <field name="act_window_id" ref="action_view_mailgate_thread"/> - </record> - - <act_window domain="[('partner_id', '=', active_id), ('history', '=', True)]" - id="act_res_partner_emails" name="Emails" - res_model="mailgate.message" - src_model="res.partner" - view_id="view_mailgate_message_tree" - /> - - <act_window - id="act_res_partner_open_email" name="Attachments" - res_model="ir.attachment" - src_model="mailgate.message" - domain="[('res_id', '=', res_id),('res_model','=',model)]"/> - - <menuitem id="base.menu_email_gateway_form" - parent="base.menu_mail_gateway" action="action_view_mail_message" /> - - </data> -</openerp> diff --git a/addons/mail_gateway/res_partner_view.xml b/addons/mail_gateway/res_partner_view.xml deleted file mode 100644 index b3d956f9fc1cad0022ede66e8f99e46bf143d302..0000000000000000000000000000000000000000 --- a/addons/mail_gateway/res_partner_view.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0"?> -<openerp> - <data> - - <!-- Partners inherited form --> - <record id="base.view_crm_partner_info_History" model="ir.ui.view"> - <field name="name">res.partner.crm.history.inherit1</field> - <field name="model">res.partner</field> - <field name="type">form</field> - <field name="inherit_id" ref="base.view_partner_form"/> - <field name="arch" type="xml"> - <xpath expr="/form/notebook/page[@string='History']" position="attributes"> - <attribute name="invisible">False</attribute> - </xpath> - </field> - </record> - <record id="view_emails_partner_info_form" model="ir.ui.view"> - <field name="name">res.partner.emails.info.inherit</field> - <field name="model">res.partner</field> - <field name="type">form</field> - <field name="inherit_id" ref="base.view_partner_form"/> - <field name="arch" type="xml"> - <page string="History" position="inside"> - <field name="emails" colspan="4" nolabel="1"/> - </page> - </field> - </record> - </data> -</openerp> - diff --git a/addons/mail_gateway/security/ir.model.access.csv b/addons/mail_gateway/security/ir.model.access.csv deleted file mode 100644 index 09f13d57b31da512e2f38c3b5294e96a05a18c0f..0000000000000000000000000000000000000000 --- a/addons/mail_gateway/security/ir.model.access.csv +++ /dev/null @@ -1,5 +0,0 @@ -"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"mail_gateway_mailgate_message","mail_gateway.mailgate.message","model_mailgate_message","base.group_system",1,1,1,1 -"mail_gateway_mailgate_thread","mail_gateway.mailgate.thread","model_mailgate_thread","base.group_system",1,1,1,1 -"mail_gateway_message_internal_user","mail_gateway.mailgate.message.internal","model_mailgate_message","base.group_user",1,0,0,0 -"mail_gateway_mailgate_message_partner_manager","mail_gateway.mailgate.message.partner.manager","model_mailgate_message","base.group_partner_manager",1,1,1,1 diff --git a/addons/marketing_campaign/marketing_campaign.py b/addons/marketing_campaign/marketing_campaign.py index ae1ab2309e2cc72db571394370d87781f2c6083b..27c91fcc3fe133fa8d673bf32c1d18f7c8063536 100644 --- a/addons/marketing_campaign/marketing_campaign.py +++ b/addons/marketing_campaign/marketing_campaign.py @@ -144,13 +144,6 @@ Normal - the campaign runs normally and automatically sends all emails and repor if activity.signal and len(activity.from_ids) == 0: has_signal_without_from = True - if activity.type != 'email': - continue - if not activity.email_template_id.from_account: - raise osv.except_osv(_("Error"), _("The campaign cannot be started: the email account is missing in email activity '%s'")%activity.name) - if activity.email_template_id.from_account.state != 'approved': - raise osv.except_osv(_("Error"), _("The campaign cannot be started: the email account is not approved in email activity '%s'")%activity.name) - if not has_start and not has_signal_without_from: raise osv.except_osv(_("Error"), _("The campaign cannot be started: it doesn't have any starting activity (or any activity with a signal and no previous activity)")) @@ -484,9 +477,9 @@ class marketing_campaign_activity(osv.osv): return True def _process_wi_email(self, cr, uid, activity, workitem, context=None): - return self.pool.get('email.template').generate_mail(cr, uid, + return self.pool.get('email.template').send_mail(cr, uid, activity.email_template_id.id, - [workitem.res_id], context=context) + workitem.res_id, context=context) def _process_wi_action(self, cr, uid, activity, workitem, context=None): if context is None: @@ -807,7 +800,7 @@ class marketing_campaign_workitem(osv.osv): 'type': 'ir.actions.act_window', 'target': 'new', 'nodestroy':True, - 'context': "{'template_id':%d,'default_rel_model_ref':%d}"% + 'context': "{'template_id':%d,'default_res_id':%d}"% (wi_obj.activity_id.email_template_id.id, wi_obj.res_id) } @@ -831,7 +824,7 @@ marketing_campaign_workitem() class email_template(osv.osv): _inherit = "email.template" _defaults = { - 'object_name': lambda obj, cr, uid, context: context.get('object_id',False), + 'model_id': lambda obj, cr, uid, context: context.get('object_id',False), } # TODO: add constraint to prevent disabling / disapproving an email account used in a running campaign diff --git a/addons/marketing_campaign/marketing_campaign_view.xml b/addons/marketing_campaign/marketing_campaign_view.xml index 63bd16740ece54d971e451eab3dc0a69a73f77f1..4ecbda6c4f8837ca70bfb00a84578ae269577068 100644 --- a/addons/marketing_campaign/marketing_campaign_view.xml +++ b/addons/marketing_campaign/marketing_campaign_view.xml @@ -267,7 +267,7 @@ <field name="type"/> <group colspan='2' col='1'> <field name="email_template_id" attrs="{'required':[('type','=','email')], 'invisible':[('type','!=','email')]}" - context="{'default_object_name':object_id}" /> + context="{'default_model_id':object_id}" /> <group attrs="{'invisible':[('type','!=','report')]}" > <field name="report_id" attrs="{'required':[('type','=','report')]}" context="{'object_id':object_id}"/> <field name="report_directory_id" attrs="{'required':[('type','=','report')]}" /> diff --git a/addons/marketing_campaign/security/ir.model.access.csv b/addons/marketing_campaign/security/ir.model.access.csv index c2b7b4f4c860d323687dcd4ef06ea1c92c56bdb5..b0157b76c5116145a34d4d3c0110fafa5e7d9b08 100644 --- a/addons/marketing_campaign/security/ir.model.access.csv +++ b/addons/marketing_campaign/security/ir.model.access.csv @@ -7,7 +7,7 @@ "access_marketing_campaign_analysis_campaignadmin","campaign.analysis","model_campaign_analysis","marketing.group_marketing_user",1,1,0,0 "access_marketing_campaign_workitem_all","marketing.campaign.workitem","model_marketing_campaign_workitem","base.group_user",1,0,0,0 "access_email_template_user","email.template","model_email_template","marketing.group_marketing_user",1,1,0,0 -"access_email_template_account_user","email_template.account.user","email_template.model_email_template_account","marketing.group_marketing_user",1,1,0,0 +"access_email_template_account_user","ir.mail_server","base.model_ir_mail_server","marketing.group_marketing_user",1,1,0,0 "access_marketing_campaign_system","marketing.campaign system","model_marketing_campaign","base.group_system",1,0,0,0 "access_marketing_campaign_segment_system","marketing.campaign.segment system","model_marketing_campaign_segment","base.group_system",1,0,0,0 "access_marketing_campaign_workitem_system","marketing.campaign.workitem system","model_marketing_campaign_workitem","base.group_system",1,0,0,0 @@ -18,4 +18,4 @@ "access_marketing_campaign_transition_campaign_manager","marketing.campaign.transition","model_marketing_campaign_transition","marketing.group_marketing_manager",1,1,1,1 "access_marketing_campaign_analysis_campaign_manager","campaign.analysis","model_campaign_analysis","marketing.group_marketing_manager",1,1,1,1 "access_email_template_manager","email.template","model_email_template","marketing.group_marketing_manager",1,1,1,1 -"access_email_template_account_manager","email_template.account.manager","email_template.model_email_template_account","marketing.group_marketing_manager",1,1,1,1 +"access_email_template_account_manager","ir.mail_server","base.model_ir_mail_server","marketing.group_marketing_manager",1,1,1,1 diff --git a/addons/outlook/__openerp__.py b/addons/outlook/__openerp__.py index 53bd587583ce4513bc6e0118333e61eaf216458c..e376bcfa34a50c809146957b11c393595036ddb2 100644 --- a/addons/outlook/__openerp__.py +++ b/addons/outlook/__openerp__.py @@ -25,17 +25,15 @@ 'version' : '1.0', 'author' : 'OpenERP SA', 'website' : 'http://www.openerp.com/', - 'depends' : ['base', 'mail_gateway'], + 'depends' : ['base', 'mail'], 'category' : 'Tools', 'description': ''' This module provides the Outlook Plug-in. ========================================= - Outlook plug-in allows you to select an object that you’d like to add to your email and its attachments from MS Outlook. You can select a partner, a task, a project, an analytical account, or any other object and archive selected -mail in mailgate.messages with attachments. - +mail into email.messages with attachments. ''', 'init_xml' : [], 'demo_xml' : [], diff --git a/addons/outlook/plugin/openerp-outlook-plugin/dialogs/dialog_map.py b/addons/outlook/plugin/openerp-outlook-plugin/dialogs/dialog_map.py index a05220ad7e08546d6ddb23f65a731a1eafeea7a3..87138554e165f6ac3738c5bc58325368e6a3cbe9 100644 --- a/addons/outlook/plugin/openerp-outlook-plugin/dialogs/dialog_map.py +++ b/addons/outlook/plugin/openerp-outlook-plugin/dialogs/dialog_map.py @@ -186,7 +186,6 @@ def check(): win32ui.MessageBox("No server running on host "+ server+" at port "+str(port), "OpenERP Connection", flag_excl) return False if str(NewConn.getitem('_login')) == 'False': - win32ui.MessageBox("Please login to the database first", "OpenERP Connection", flag_excl) return False return True diff --git a/addons/outlook/plugin/openerp-outlook-plugin/dialogs/resources/dialogs.py b/addons/outlook/plugin/openerp-outlook-plugin/dialogs/resources/dialogs.py index 247b9c7a5332b44a1e076d5347897ebfc2309a2e..aa417b2cd59d8484f368aadcb2d4d8c7a23fc4b1 100644 --- a/addons/outlook/plugin/openerp-outlook-plugin/dialogs/resources/dialogs.py +++ b/addons/outlook/plugin/openerp-outlook-plugin/dialogs/resources/dialogs.py @@ -1,7 +1,7 @@ -#C:\workspace\openerp-outlook-plugin\dialogs\resources\dialogs.py -#This is a generated file. Please edit C:\workspace\openerp-outlook-plugin\dialogs\resources\dialogs.rc instead. +#C:\Program Files\Openerp Outlook Addin\dialogs\resources\dialogs.py +#This is a generated file. Please edit C:\Program Files\Openerp Outlook Addin\dialogs\resources\dialogs.rc instead. _rc_size_=16350 -_rc_mtime_=1296563638 +_rc_mtime_=1296608638 class FakeParser: dialogs = {'IDD_OPEN_DOCUEMNT_DIALOG': [['Open Document', (0, 0, 200, 65), -1865940928, 1024, (8, 'Tahoma')], [130, 'Link to Document : (If this link does not opens directly in web browser then copy an the link and paste in web browser.) ', -1, (5, 3, 190, 25), 1342177280], [129, '', 2086, (5, 31, 190, 12), 1350631552], [128, 'Ok', 2, (150, 45, 45, 14), 1342242816]], 'IDD_VIEW_PARTNER_DIALOG': [['Open Contact', (0, 0, 350, 215), -1865940928, 1024, (8, 'Tahoma')], [130, 'Email ID : ', -1, (32, 17, 40, 12), 1342177280], [129, '', 2051, (70, 15, 200, 12), 1350631552], [128, 'Search Contact ', 2052, (280, 15, 60, 14), 1342242816], [130, 'Partner Name : ', -1, (13, 42, 50, 17), 1342177280], [129, '', 2064, (70, 42, 150, 12), 1350568064], [128, 'Search Partner', 2070, (225, 41, 60, 14), 1342242816], [128, 'New Partner', 2093, (290, 41, 50, 14), 1342242816], [128, 'Postal Address ', 2024, (10, 65, 175, 125), 1342177287], [130, 'Contact Name : ', -1, (18, 83, 50, 17), 1342177280], [129, '', 2046, (76, 82, 100, 12), 1350631552], [130, 'Street : ', -1, (18, 97, 50, 17), 1342177280], [129, '', 2053, (76, 96, 100, 12), 1350631552], [130, 'Street2 : ', -1, (18, 112, 50, 17), 1342177280], [129, '', 2054, (76, 111, 100, 12), 1350631552], [130, 'Zip : ', -1, (18, 126, 50, 14), 1342177280], [129, '', 40010, (76, 125, 100, 12), 1350631552], [130, 'City : ', -1, (18, 140, 50, 17), 1342177280], [129, '', 40011, (76, 139, 100, 12), 1350631552], [130, 'Fed. State : ', -1, (18, 154, 50, 17), 1342177280], [129, '', 2062, (76, 153, 54, 12), 1350568064], [128, 'Search', 2104, (135, 153, 40, 12), 1342242816], [130, 'Country :', -1, (18, 169, 35, 17), 1342177280], [129, '', 2063, (76, 168, 54, 12), 1350568064], [128, 'Search', 2105, (135, 168, 40, 12), 1342242816], [128, 'Communication ', 2024, (188, 65, 152, 125), 1342177287], [130, 'Phone : ', -1, (194, 83, 30, 17), 1342177280], [129, '', 2048, (224, 82, 100, 12), 1350631552], [130, 'Mobile : ', -1, (194, 97, 30, 17), 1342177280], [129, '', 2050, (224, 96, 100, 12), 1350631552], [130, 'Email : ', -1, (194, 111, 30, 17), 1342177280], [129, '', 2058, (224, 110, 100, 12), 1350568064], [130, 'Fax : ', -1, (194, 125, 30, 17), 1342177280], [129, '', 2047, (224, 124, 100, 12), 1350631552], [128, 'Create a New Contact', 2071, (124, 195, 82, 14), 1342242816], [128, 'Save', 2059, (213, 195, 60, 14), 1342242816], [128, 'Cancel', 2, (281, 195, 60, 14), 1342242816]], 'IDD_OPEN_PARTNER_DIALOG': [['Open Partner', (0, 0, 200, 65), -1865940928, 1024, (8, 'Tahoma')], [130, 'Link to Partner : (If this link does not opens directly in web browser then copy an the link and paste in web browser.) ', -1, (5, 3, 190, 25), 1342177280], [129, '', 2085, (5, 31, 190, 12), 1350631552], [128, 'Ok', 2, (150, 45, 45, 13), 1342242816]], 'IDD_SELECT_STATE': [['Search Fed. State', (0, 0, 220, 250), -1865940928, 1024, (8, 'Tahoma')], [130, 'Enter Name : ', -1, (8, 12, 80, 17), 1342177280], [129, '', 2100, (55, 10, 100, 12), 1350631552], [128, 'Search', 40012, (160, 10, 50, 14), 1342242816], ['SysListView32', 'List1', 2101, (8, 40, 200, 185), 1353711657], [128, 'Select', 2102, (170, 230, 40, 14), 1342242816]], 'IDD_GENERAL': [['Connection Parameters', (0, 0, 430, 210), 1355284544, None, (8, 'Tahoma')], [128, 'Connection Parameters', 2024, (10, 10, 250, 130), 1342177287], [130, 'Server : ', -1, (30, 30, 100, 17), 1342177280], [129, '', 2003, (85, 30, 120, 12), 1350633472], [128, 'Change', 2021, (210, 30, 30, 13), 1342242816], [130, 'Database : ', -1, (30, 50, 100, 17), 1342177280], [133, '', 2004, (85, 50, 154, 40), 1344339971], [130, 'Username : ', -1, (30, 70, 100, 17), 1342177280], [129, '', 2005, (85, 70, 154, 12), 1350631552], [130, 'Password : ', -1, (30, 90, 100, 17), 1342177280], [129, '', 2006, (85, 90, 154, 12), 1350631584], [128, 'Connect', 2007, (179, 110, 60, 13), 1342242816], [128, 'WebServer Parameters', 2024, (270, 10, 150, 130), 1342177287], [130, 'Server : ', -1, (280, 30, 50, 15), 1342177280], [129, '', 2080, (310, 29, 90, 12), 1350568064], [128, 'Change', 2082, (295, 50, 50, 12), 1342242816], [128, 'Connect', 2083, (350, 50, 50, 12), 1342242816]], 'IDD_OBJECT_SETTINGS': [['Documents Setting', (0, 0, 430, 210), 1355284672, 1024, (8, 'Tahoma')], [128, 'Document Attributes', 2024, (2, 2, 343, 32), 1342177287], [130, 'Title:', -1, (5, 16, 30, 17), 1342177280], [129, '', 2013, (22, 14, 57, 12), 1350631552], [130, 'Document Name:', -1, (83, 16, 60, 17), 1342177280], [129, '', 2014, (138, 14, 57, 12), 1350631552], [130, 'Image:', -1, (200, 16, 22, 17), 1342177280], [129, '', 2015, (223, 14, 57, 12), 1350631552], [128, 'Load Image', 2010, (289, 13, 50, 15), 1342242816], [128, 'Add', 2011, (350, 13, 38, 15), 1342242816], [128, 'Delete', 2012, (392, 13, 38, 15), 1342242816], ['SysListView32', 'List1', 2016, (8, 40, 535, 160), 1353711657]], 'IDD_NEW_PARTNER_DIALOG': [['Create a New Partner', (0, 0, 140, 40), -1865940928, 1024, (8, 'Tahoma')], [130, 'Name : ', -1, (5, 3, 100, 17), 1342177280], [129, '', 2036, (40, 3, 94, 12), 1350631552], [128, 'Cancel', 2, (90, 22, 45, 14), 1342242816], [128, 'Save', 2035, (40, 22, 45, 14), 1342242817]], 'IDD_MANAGER': [['OpenERP Configuration', (0, 0, 460, 260), -1865940800, None, (8, 'Tahoma')], [128, 'Close', 2008, (400, 239, 50, 14), 1342177281], ['SysTabControl32', '', 1068, (8, 7, 440, 228), 1342177280], ['SysListView32', '', 2016, (0, 0, 0, 0), 1353711657]], 'IDD_WEB_SERVER_PORT_DIALOG': [['OpenERP Connection', (0, 0, 160, 80), -1865940928, 1024, (8, 'Tahoma')], [130, 'Server : ', -1, (5, 3, 150, 17), 1342177280], [129, '', 2089, (40, 3, 100, 12), 1350631552], [130, 'Port : ', -1, (5, 18, 100, 17), 1342177280], [129, '', 2090, (40, 18, 100, 12), 1350631552], [128, 'Close', 2, (110, 50, 45, 14), 1342242816], [128, 'OK', 2091, (50, 50, 50, 14), 1342242817], [128, 'SSL (https)', 2111, (20, 35, 100, 14), 1342242819]], 'IDD_ABOUT': [['About', (0, 0, 430, 210), 1355284672, 1024, (8, 'Tahoma')], [128, 'About Plugin', -1, (7, 3, 422, 200), 1342177287], [130, '1062', 1062, (60, 30, 20, 20), 1342179342], [130, '', 2028, (80, 90, 300, 100), 1342177280]], 'IDD_NEW_CONTACT_DIALOG': [['Create a New Contact', (0, 0, 350, 180), -1865940928, 1024, (8, 'Tahoma')], [130, 'Select Partner : ', -1, (13, 20, 50, 17), 1342177280], [129, '', 2079, (70, 18, 150, 12), 1350568064], [128, 'Search Partner', 2033, (225, 17, 60, 14), 1342242816], [128, 'New Partner', 2092, (290, 17, 50, 14), 1342242816], [128, 'Postal Address ', 2024, (10, 35, 175, 123), 1342177287], [130, 'Contact Name : ', -1, (18, 53, 100, 17), 1342177280], [129, '', 40005, (76, 52, 100, 12), 1350631552], [130, 'Street : ', -1, (18, 67, 40, 17), 1342177280], [129, '', 2062, (76, 66, 100, 12), 1350631552], [130, 'Street2 : ', -1, (18, 81, 40, 17), 1342177280], [129, '', 2063, (76, 80, 100, 12), 1350631552], [130, 'Zip : ', -1, (18, 94, 50, 17), 1342177280], [129, '', 2068, (76, 93, 100, 12), 1350631552], [130, 'City : ', -1, (18, 106, 50, 17), 1342177280], [129, '', 2067, (76, 107, 100, 12), 1350631552], [130, 'Fed. State : ', -1, (18, 134, 50, 15), 1342177280], [129, '', 2106, (76, 135, 54, 12), 1350568064], [128, 'Search', 2107, (135, 136, 40, 12), 1342242816], [130, 'Country :', -1, (18, 121, 35, 15), 1342177280], [129, '', 2108, (76, 122, 54, 12), 1350568064], [128, 'Search', 2109, (135, 121, 40, 12), 1342242816], [128, 'Communication ', 2024, (188, 35, 150, 123), 1342177287], [130, 'Office : ', -1, (194, 53, 30, 17), 1342177280], [129, '', 40006, (224, 52, 100, 12), 1350631552], [130, 'Mobile : ', -1, (194, 67, 30, 17), 1342177280], [129, '', 40007, (224, 66, 100, 12), 1350631552], [130, 'Fax : ', -1, (194, 81, 30, 17), 1342177280], [129, '', 2066, (224, 80, 100, 12), 1350631552], [130, 'Email : ', -1, (194, 95, 30, 17), 1342177280], [129, '', 40008, (224, 94, 100, 12), 1350631552], [128, 'Cancel', 2, (290, 162, 45, 13), 1342242816], [128, 'Save', 40009, (240, 162, 45, 13), 1342242816]], 'IDD_SERVER_PORT_DIALOG': [['OpenERP Connection', (0, 0, 160, 90), -1865940928, 1024, (8, 'Tahoma')], [130, 'Server : ', -1, (5, 3, 150, 17), 1342177280], [129, '', 2001, (45, 3, 100, 12), 1350631552], [130, ' Port : ', -1, (5, 18, 100, 17), 1342177280], [129, '', 2002, (45, 18, 100, 12), 1350631552], [128, '', 2024, (5, 35, 150, 5), 1342177287], [130, 'Protocol Connection :', -1, (5, 45, 80, 17), 1342177280], [133, '', 2110, (75, 43, 75, 50), 1344339971], [128, 'Close', 2, (60, 70, 45, 14), 1342242816], [128, 'OK', 1, (110, 70, 45, 14), 1342242817]], 'IDD_SELECT_PARTNER': [['Search Partner', (0, 0, 220, 250), -1865940928, 1024, (8, 'Tahoma')], [130, 'Enter Name : ', -1, (8, 12, 80, 17), 1342177280], [129, '', 2076, (55, 10, 100, 12), 1350631552], [128, 'Search', 2077, (160, 10, 50, 14), 1342242816], ['SysListView32', 'List1', 2072, (8, 40, 200, 185), 1353711657], [128, 'Create New Partner', 2078, (10, 230, 100, 14), 1342242816], [128, 'Select', 2075, (170, 230, 40, 14), 1342242816]], 'IDD_SYNC': [['Push to OpenERP', (0, 0, 470, 320), -1865940928, 1024, (8, 'Tahoma')], [128, 'Link to an Existing Documents ', 2024, (8, 5, 250, 290), 1342242823], [130, 'Search : ', -1, (15, 17, 40, 12), 1342177280], [129, '', 40003, (60, 15, 120, 12), 1350631552], [128, 'Search', 40004, (187, 15, 40, 14), 1342242816], [130, 'Documents : ', -1, (15, 140, 100, 14), 1342177280], ['SysListView32', 'List1', 2026, (15, 150, 234, 110), 1350631433], [128, 'Push', 2019, (160, 270, 85, 14), 1342242816], [128, ' Create a New Document ', 2024, (263, 5, 202, 100), 1342242823], [130, 'Type of Document : ', -1, (266, 25, 100, 12), 1342177280], [133, '', 2025, (332, 24, 75, 45), 1344339971], [128, 'Create', 2020, (412, 23, 50, 14), 1342242816], [128, ' Create a New Contact ', 2024, (263, 110, 202, 185), 1342242823], [130, 'Create a New Contact : ', -1, (280, 140, 100, 12), 1342177280], [128, 'Create Contact', 2018, (360, 138, 60, 14), 1342242816], [128, 'Close', 2, (385, 300, 60, 14), 1342242816]], 'IDD_SELECT_COUNTRY': [['Search Country', (0, 0, 220, 250), -1865940928, 1024, (8, 'Tahoma')], [130, 'Enter Name : ', -1, (8, 12, 80, 17), 1342177280], [129, '', 2095, (55, 10, 100, 12), 1350631552], [128, 'Search', 2098, (160, 10, 50, 14), 1342242816], ['SysListView32', 'List1', 2096, (8, 40, 200, 185), 1353711657], [128, 'Select', 2097, (170, 230, 40, 14), 1342242816]]} ids = {'IDC_BUT_DEL_OBJECT': 2012, 'IDD_OPEN_PARTNER_DIALOG': 2084, 'IDD_SELECT_STATE': 2099, 'IDC_DELAY1_SLIDER': 1056, 'IDC_PROGRESS': 1000, 'IDD_MANAGER': 101, 'IDC_ABOUT': 2028, 'IDD_DIAGNOSTIC': 113, 'IDET_PARTNER_COUNTRY': 2063, 'IDD_TRAINING': 102, 'ID_CONTACT_EMAIL_TEXT': 40008, 'IDC_DELAY2_TEXT': 1059, 'IDC_DELAY1_TEXT': 1057, 'IDD_WIZARD': 114, 'IDC_CHKBX': 2023, 'IDC_STATIC_HAM': 1002, 'IDC_PROGRESS_TEXT': 1001, 'IDR_XMLS_PROTOCOL': 2042, 'IDD_GENERAL': 108, 'IDD_ABOUT': 2027, 'IDD_SYNC': 40002, 'IDC_TAB': 1068, 'IDC_FOLDER_UNSURE': 1033, '_APS_NEXT_SYMED_VALUE': 101, 'IDC_VERBOSE_LOG': 1061, 'IDC_EDIT1': 1094, 'IDC_BROWSE': 1037, 'ID_DB_DROPDOWNLIST': 2004, 'IDC_BACK_BTN': 1069, 'ID_CONTACT_NAME_TEXT': 40005, 'IDPB_PARTNER_SEARCH': 2077, 'IDD_WIZARD_FINISHED_UNCONFIGURED': 119, 'IDC_ACTION_CERTAIN': 1025, 'IDC_BUT_ACT_ALL': 1019, 'IDD_FILTER_NOW': 104, 'IDET_WED_SERVER': 2080, 'ID_WEB_OK': 2091, 'ID_PROTOCOL_GRP': 2040, 'IDC_HEADER': 2017, 'IDC_MARK_SPAM_AS_READ': 1047, 'IDPB_NEW_PARTNER_BUTTON': 2092, 'ID_PARTNER_NAME_TEXT': 2036, 'IDC_RECOVER_RS': 1075, 'IDC_NAME_LIST1': 2037, 'ID_CONTACT_OFFICE_TEXT': 40006, 'IDC_STATIC': -1, 'IDC_PAGE_PLACEHOLDER': 1078, 'IDC_BROWSE_WATCH': 1039, 'IDET_ZIP': 40010, 'IDET_PARTNER_CITY': 40011, 'IDET_PARTNER_LINK_TEXT': 2085, 'IDC_FOLDER_HAM': 1083, 'IDC_LIST_COUNTRY': 2096, 'IDD_WIZARD_FOLDERS_REST': 117, 'IDC_SHOW_DATA_FOLDER': 1071, 'IDC_BUT_ACT_SCORE': 1018, 'IDET_STATE_SEARCH_NAME': 2100, 'IDET_WEB_SERVER': 2089, '_APS_NEXT_RESOURCE_VALUE': 128, 'ID_SET_WEB_CONNECTION': 2082, 'ID_DROPDOWNLIST_PROTOCOL': 2110, 'IDC_LIST_STATE': 2101, 'IDC_CONTACT_LIST': 2029, 'IDC_SLIDER_CERTAIN': 1023, 'IDET_PARTNER_STREET': 2053, 'IDC_BUT_UNREAD': 1020, 'ID_PARTNER_DROPDOWNLIST': 2032, 'ID_COUNTRY_DROPLIST': 2065, 'IDC_BUT_ABOUT': 1017, 'IDC_BUT_RESCORE': 1008, 'IDC_BUT_SEARCHSUB': 1041, 'IDC_BUT_TRAIN_FROM_SPAM_FOLDER': 1010, 'IDET_PARTNER_MOBILENO': 2050, 'ID_CONTACT_MOBILE_TEXT': 40007, 'IDD_VIEW_PARTNER_DIALOG': 2044, 'IDD_WIZARD_FOLDERS_TRAIN': 120, 'IDC_BUT_FILTER_ENABLE': 1013, 'IDPB_WEB_CONNECTION': 2083, 'IDC_ABOUT_BTN': 1072, 'IDD_WIZARD_FINISHED_TRAINED': 122, 'ID_SERVER': 2001, 'IDD_SELECT_PARTNER': 2073, 'IDET_NC_PARTNER_STATE': 2106, 'IDD_FOLDER_SELECTOR': 105, 'IDC_BUT_SET_SERVER_PORT': 2021, 'ID_DONE': 2008, 'IDC_LIST_FOLDERS': 1040, 'IDC_IMAGE_PATH': 2015, 'IDB_SBWIZLOGO': 125, 'IDB_OPENERPLOGO': 1062, 'ID_ZIP_TEXT': 2068, 'IDEB_OPENDOC_LINK_TEXT': 2086, 'IDD_NEW_PARTNER_DIALOG': 2034, 'ID_PARTNER_DROPLIST': 2069, 'IDC_BUT_VIEW_LOG': 1093, 'IDC_STATUS2': 1044, 'IDC_STATUS1': 1043, 'IDCANCEL': 2, 'IDC_BROWSE_HAM': 1004, 'ID_BUT_TESTCONNECTION': 2007, 'IDR_NETRPC_PROTOCOL': 2043, 'IDC_BROWSE_SPAM': 1005, 'IDCB_WEB_SECURE': 2111, 'IDD_OPEN_DOCUEMNT_DIALOG': 2087, 'IDD_WIZARD_FINISHED_UNTRAINED': 116, 'IDC_MARK_UNSURE_AS_READ': 1051, 'IDPB_SEARCH_COUNTRY1': 2105, 'ID_PARTNER_CITY_TEXT': 2067, 'IDC_BUT_WIZARD': 1070, 'IDC_VERSION': 1009, 'ID_NEW_PARTNER_BUTTON': 2033, 'IDC_FOLDER_NAMES': 1036, 'ID_ATT_METHOD_DROPDOWNLIST': 2025, 'IDC_BUT_TIMER_ENABLED': 1091, 'IDPB_WRITE_CHANGES': 2059, 'IDC_SLIDER_UNSURE': 1029, 'IDC_BUT_NEW': 1046, 'IDC_FOLDER_WATCH': 1038, 'IDPB_CREATE_NEW_PARTNER': 2078, 'IDC_BUT_UNTRAINED': 1088, 'IDC_STATIC_SPAM': 1003, 'IDD_NEW_CONTACT_DIALOG': 2031, 'IDC_EDIT_UNSURE': 1030, 'IDC_BUT_CLEARALL': 1042, 'IDC_BUT_UNSEEN': 1021, 'IDC_OBJECT_NAME': 2014, 'IDD_WIZARD_FOLDERS_WATCH': 118, 'IDPB_SEARCH_STATE': 2103, 'IDET_COUNTRY_SEARCH_NAME': 2095, 'IDC_BUT_SAVE_OBJECT': 2011, 'ID_FED_STATE_DROPLIST': 2064, 'ID_ALL_COUNTRY_DROPDOWNLIST': 2061, 'IDC_EDIT_CERTAIN': 1024, 'IDC_BUT_FILTER_DEFINE': 1016, 'ID_NEW_PART_BUTTON': 2070, 'ID_FAX_TEXT': 2066, 'IDD_WIZARD_TRAINING_IS_IMPORTANT': 123, 'ID_ALL_STATE_DROPDOWNLIST': 2060, 'IDPB_SEARCH_STATE1': 2104, 'IDC_INBOX_TIMER_ONLY': 1060, 'IDPB_SEARCH_PARTNER': 2052, 'ID_USERNAME': 2005, '_APS_NEXT_CONTROL_VALUE': 1096, 'IDC_WIZ_GRAPHIC': 1092, 'IDD_OBJECT_SETTINGS': 2009, 'IDD_FILTER_UNSURE': 111, 'IDC_DEL_SPAM_RS': 1074, 'IDD_SELECT_COUNTRY': 2094, 'ID_SERVER_PORT': 2003, 'IDR_XML_PROTOCOL': 2041, 'IDET_PARTNER_OFFICENO': 2048, 'IDB_FOLDERS': 127, 'IDC_BUT_PREPARATION': 1081, 'ID_STREET2_TEXT': 2063, 'IDC_DELAY2_SLIDER': 1058, 'IDET_PARTNER': 2064, 'IDC_SAVE_SPAM_SCORE': 1048, 'IDC_OBJECT_TITLE': 2013, 'IDC_FOLDER_CERTAIN': 1027, 'IDET_WEB_PORT': 2090, 'IDC_BROWSE_UNSURE': 1034, 'IDC_STATISTICS': 1095, 'IDPB_STATE_SEARCH': 40012, 'ID_MAKE_ATTACHMENT': 2019, 'IDC_BUT_LOAD_IMAGE': 2010, 'IDC_NAME_LIST': 2026, 'IDC_BUT_TRAIN_TO_SPAM_FOLDER': 1011, 'IDET_PARTNER_SEARCH_NAME': 2076, 'IDC_BUT_RESET': 1073, 'ID_SEARCH': 40004, 'IDET_PARTNER_CONTACT_NAME': 2046, 'IDPB_SELECT_STATE': 2102, 'IDC_ACTION_UNSURE': 1031, 'IDD_WIZARD_TRAIN': 121, 'IDC_STATIC_GROUP': 2024, 'IDPB_NEWPARTNER_BUTTON': 2071, 'ID_PARTNER_TEXT': 2079, 'IDD_WIZARD_FINISHED_TRAIN_LATER': 124, 'IDET_PARTNER_NAME': 2045, 'IDPB_NEW_PART_BUTTON': 2093, 'IDC_BUT_REBUILD': 1007, 'IDET_PARTNER_STREET2': 2054, 'IDPB_SEARCH_COUNTRY': 2098, 'ID_SAVE_PARTNER_BUTTON': 2035, 'IDPB_SELECT_PARTNER': 2075, 'ID_STREET_TEXT': 2062, '_APS_NEXT_COMMAND_VALUE': 40001, 'IDET_SEARCH_PARTNER': 2051, 'IDC_LIST_PARTNER': 2072, 'IDD_WEB_SERVER_PORT_DIALOG': 2088, 'ID_PORT': 2002, 'IDD_SERVER_PORT_DIALOG': 2022, 'IDET_PARTNER_STATE': 2062, 'IDPB_SELECT_COUNTRY': 2097, 'ID_SEARCH_TEXT': 40003, 'IDET_NC_PARTNER_COUNTRY': 2108, 'ID_CREATE_CONTACT': 2018, 'IDC_FORWARD_BTN': 1077, 'IDC_TRAINING_STATUS': 1035, 'IDD_WIZARD_WELCOME': 115, 'IDET_PARTNER_FAX': 2058, 'IDC_BUT_TRAIN': 1089, 'IDC_LIST': 2016, 'ID_CREATE_CASE': 2020, 'IDET_PARTNER_EMAIL': 2047, 'IDC_START': 1006, 'IDD_FILTER': 103, 'IDC_RELOAD': 2038, 'ID_PASSWORD': 2006, 'ID_NAME_TEXT': 2030, 'IDC_FILTER_STATUS': 1014, 'IDPB_NC_SEARCH_STATE1': 2107, 'ID_CONTACT_SAVE_BUTTON': 40009, 'IDOK': 1, 'IDC_BROWSE_CERTAIN': 1028, 'IDC_BUT_SHOW_DIAGNOSTICS': 1080, 'IDC_BUT_TRAIN_NOW': 1012, 'IDPB_NC_SEARCH_COUNTRY1': 2109} diff --git a/addons/outlook/plugin/openerp-outlook-plugin/tiny_xmlrpc.py b/addons/outlook/plugin/openerp-outlook-plugin/tiny_xmlrpc.py index 7ecb2aea81fec03f1b0414c73e38dd331c336e2b..41346fe7cf94cec98af2e8a9e390338475f21190 100644 --- a/addons/outlook/plugin/openerp-outlook-plugin/tiny_xmlrpc.py +++ b/addons/outlook/plugin/openerp-outlook-plugin/tiny_xmlrpc.py @@ -20,6 +20,8 @@ ############################################################################## import xmlrpclib +import binascii +import base64 import sys import socket import os @@ -146,6 +148,7 @@ class XMLRpcConn(object): flag = False new_msg = ext_msg ="" message_id = referances = None + context = {} try: session = win32com.client.Dispatch("MAPI.session") session.Logon('Outlook') @@ -177,15 +180,15 @@ class XMLRpcConn(object): for rec in recs: #[('res.partner', 3, 'Agrolait')] model = rec[0] res_id = rec[1] - #Check if mailgate installed - object_id = execute ( conn,'execute',self._dbname,int(self._uid),self._pwd,'ir.model','search',[('model','=','mailgate.message')]) + #Check if mail installed + object_id = execute ( conn,'execute',self._dbname,int(self._uid),self._pwd,'ir.model','search',[('model','=','mail.message')]) if not object_id: - win32ui.MessageBox("Mailgate is not installed on your configured database '%s' !!\n\nPlease install it to archive the mail."%(self._dbname),"Mailgate not installed",win32con.MB_ICONERROR) + win32ui.MessageBox("Mail is not installed on your configured database '%s' !!\n\nPlease install it to archive the mail."%(self._dbname),"Mail not installed",win32con.MB_ICONERROR) return object_ids = execute ( conn,'execute',self._dbname,int(self._uid),self._pwd,'ir.model','search',[('model','=',model)]) object_name = execute( conn,'execute',self._dbname,int(self._uid),self._pwd,'ir.model','read',object_ids,['name'])[0]['name'] #Reading the Object ir.model Name - ext_ids = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,'mailgate.message','search',[('message_id','=',message_id),('model','=',model),('res_id','=',res_id)]) + ext_ids = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,'mail.message','search',[('message_id','=',message_id),('model','=',model),('res_id','=',res_id)]) if ext_ids: name = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,model,'read',res_id,['name'])['name'] ext_msg += """This mail is already archived to {0} '{1}'.\n""".format(object_name,name) @@ -201,14 +204,30 @@ class XMLRpcConn(object): 'message-id':message_id, 'references':ustr(referances), } - obj_list= ['crm.lead','project.issue','hr.applicant','res.partner'] - if rec[0] not in obj_list: - self.CreateEmailAttachment(rec,mail) result = {} + context['thread_model'] = model if attachments: result = self.MakeAttachment([rec], mail) - attachment_ids = result.get(model, {}).get(res_id, []) - execute(conn,'execute',self._dbname,int(self._uid),self._pwd,'email.server.tools','history',model, res_id, msg, attachment_ids) + execute(conn, 'execute', self. _dbname, int(self._uid), self._pwd, + 'mail.thread','message_append',[res_id], + msg.get('subject', False), + msg.get('body', False), + msg.get('to', False), + msg.get('from', False), + msg.get('cc', False), + False, # BCC + False, #reply-to + msg.get('date', False), + msg.get('message-id'),\ + msg.get('references', False), + result, #attachments + #FIXME: properly handle plaintext/html body variants? + False, #body_html + False, #subtype + False, #headers + False, #original + context) + new_msg += """- {0} : {1}\n""".format(object_name,str(rec[2])) flag = True @@ -298,7 +317,7 @@ class XMLRpcConn(object): endCut = message_id.find(">") message_id = message_id[startCut:endCut+1] email.replace_header('Message-Id',message_id) - id = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,'email.server.tools','process_email',section, str(email)) + id = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,'mail.thread','message_process',section, str(email)) if id > 0: flag = True return flag @@ -310,39 +329,28 @@ class XMLRpcConn(object): return flag def MakeAttachment(self, recs, mail): - attachments = mail.Attachments - result = {} - conn = xmlrpclib.ServerProxy(self._uri+ '/xmlrpc/object') - att_folder_path = os.path.abspath(os.path.dirname("%temp%\\")) - if not os.path.exists(att_folder_path): + attachments = mail.Attachments + attachment = {} + conn = xmlrpclib.ServerProxy(self._uri+ '/xmlrpc/object') + att_folder_path = os.path.abspath(os.path.dirname("%temp%\\")) + if not os.path.exists(att_folder_path): os.makedirs(att_folder_path) for rec in recs: #[('res.partner', 3, 'Agrolait')] - obj = rec[0] - obj_id = rec[1] - res={} - res['res_model'] = obj - attachment_ids = [] - if obj not in result: - result[obj] = {} - for i in xrange(1, attachments.Count+1): - fn = ustr(attachments[i].FileName) - if len(fn) > 64: - l = 64 - len(fn) - f = fn.split('.') - fn = f[0][0:l] + '.' + f[-1] - att_path = os.path.join(att_folder_path,fn) - attachments[i].SaveAsFile(att_path) - f=open(att_path,"rb") - content = "".join(f.readlines()).encode('base64') - f.close() - res['name'] = ustr(attachments[i].DisplayName) - res['datas_fname'] = ustr(fn) - res['datas'] = content - res['res_id'] = obj_id - id = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,'ir.attachment','create',res) - attachment_ids.append(id) - result[obj].update({obj_id: attachment_ids}) - return result + obj = rec[0] + obj_id = rec[1] + for i in xrange(1, attachments.Count+1): + fn = ustr(attachments[i].FileName) + if len(fn) > 64: + l = 64 - len(fn) + f = fn.split('.') + fn = f[0][0:l] + '.' + f[-1] + att_path = os.path.join(att_folder_path,fn) + attachments[i].SaveAsFile(att_path) + f=open(att_path,"rb") + content = "".join(f.readlines()) + f.close() + attachment[fn] = content + return attachment def CreateContact(self, res=None): res=eval(str(res)) @@ -456,17 +464,17 @@ class XMLRpcConn(object): import win32ui conn = xmlrpclib.ServerProxy(self._uri+ '/xmlrpc/object') res_vals = [] - mail_id = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mailgate.message', 'search', [('message_id','=',message_id)]) + mail_id = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mail.message', 'search', [('message_id','=',message_id)]) ref_mail_id = None if not mail_id: - ref_mail_id = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mailgate.message', 'search', [('references','=',message_id)]) + ref_mail_id = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mail.message', 'search', [('references','like','%'+message_id+'%')]) if ref_mail_id: - address = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mailgate.message','read',ref_mail_id[0],['model','res_id']) + address = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mail.message','read',ref_mail_id[0],['model','res_id']) for key, vals in address.items(): res_vals.append([key,vals]) return res_vals return None - address = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mailgate.message','read',mail_id[0],['model','res_id']) + address = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mail.message','read',mail_id[0],['model','res_id']) for key, vals in address.items(): res_vals.append([key,vals]) return res_vals diff --git a/addons/portal/wizard/portal_wizard.py b/addons/portal/wizard/portal_wizard.py index a18ecb2c33e62e61ff2c036dbab6fd36a55b377c..b966abfef416e52e2afa7f456b9d8023faa84641 100644 --- a/addons/portal/wizard/portal_wizard.py +++ b/addons/portal/wizard/portal_wizard.py @@ -26,7 +26,7 @@ from osv import osv, fields from tools.misc import email_re, email_send from tools.translate import _ -from base.res.res_user import _lang_get +from base.res.res_users import _lang_get diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py index a2387ce24dcef684427990e8d9074aff7008e649..936c7d361fc656d5bd3b0932c9f30e0a0e3b8928 100644 --- a/addons/project/__openerp__.py +++ b/addons/project/__openerp__.py @@ -28,7 +28,7 @@ "category": "Project Management", 'complexity': "easy", "images": ["images/gantt.png", "images/project_dashboard.jpeg","images/project_task_tree.jpeg","images/project_task.jpeg","images/project.jpeg","images/task_analysis.jpeg"], - "depends": ["base_setup", "product", "analytic", "board"], + "depends": ["base_setup", "product", "analytic", "board", "mail"], "description": """ Project management module tracks multi-level projects, tasks, work done on tasks, eso. ====================================================================================== @@ -46,7 +46,6 @@ Dashboard for project members that includes: "update_xml": [ "security/project_security.xml", "wizard/project_task_delegate_view.xml", - "wizard/project_task_close_view.xml", "wizard/project_task_reevaluate_view.xml", "security/ir.model.access.csv", "project_data.xml", diff --git a/addons/project/project.py b/addons/project/project.py index 96e39f1f1ded016f9dd64b9dc636cec8a3d7d2ab..e62570a1a43b805a30b607556b6a147fced3590d 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -590,11 +590,12 @@ class task(osv.osv): 'name': _('Send Email after close task'), 'view_type': 'form', 'view_mode': 'form', - 'res_model': 'project.task.close', + 'res_model': 'mail.compose.message', 'type': 'ir.actions.act_window', 'target': 'new', 'nodestroy': True, - 'context': {'active_id': task.id} + 'context': {'active_id': task.id, + 'active_model': 'project.task'} } return res diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index a84423a4703158a4633dba3c315e60c8b5bb8cfc..0f0c4a9ca0b0464577ced1601ff2de55c7cb1a4d 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -297,7 +297,7 @@ <field name="create_date"/> </group> <separator string="Miscelleanous" colspan="4"/> - <field name="partner_id"/> + <field name="partner_id" /> <field name="company_id" select="1" groups="base.group_multi_company" widget="selection"/> <group col="4" colspan="2"> <field name="type_id" widget="selection" readonly="1"/> @@ -312,6 +312,63 @@ </field> </record> + + + <record id="view_task_kanban" model="ir.ui.view"> + <field name="name">project.task.kanban</field> + <field name="model">project.task</field> + <field name="type">kanban</field> + <field name="arch" type="xml"> + <kanban> + <field name="state"/> + <templates> + <t t-name="kanban-box"> + <t t-set="color">#fff</t> + <t t-if="state.raw_value == 'done'" t-set="color">#dfd</t> + <t t-if="state.raw_value == 'open'" t-set="color">lightcyan</t> + <t t-if="state.raw_value == 'cancel' and total_hours.raw_value gt 0" t-set="color">red</t> + <div t-attf-style="background: #{color}"> + <table> + <tr> + <td>Title :</td> + <td><field name="name"/></td> + </tr> + <tr> + <td>Progress :</td> + <td><field name="progress"/></td> + </tr> + <tr> + <td><button data-type="action" data-name="%(action_project_task_reevaluate)d">Reevaluate</button><button data-type="edit" >Edit</button></td> + <td><button data-type="object" data-name="prev_type">Previous phase</button></td> + </tr> + <tr> + <td>Project name:</td> + <td colspan="2"><field name="project_id"/></td> + </tr> + <tr> + <td>Phase</td> + <td colspan="2"><field name="type_id"/></td> + </tr> + <tr> + <td>Remain Time :</td> + <td colspan="2"><field name="remaining_hours"/></td> + </tr> + <tr> + <td>Spent Time :</td> + <td colspan="2"><field name="remaining_hours"/></td> + </tr> + <tr> + <td>Remain Time :</td> + <td colspan="2"><field name="remaining_hours"/></td> + </tr> + </table> + </div> + </t> + </templates> + </kanban> + </field> + </record> + <record id="view_task_tree2" model="ir.ui.view"> <field name="name">project.task.tree</field> <field name="model">project.task</field> @@ -437,7 +494,7 @@ <field name="name">Tasks</field> <field name="res_model">project.task</field> <field name="view_type">form</field> - <field name="view_mode">tree,form,calendar,gantt,graph</field> + <field name="view_mode">tree,form,calendar,gantt,graph,kanban</field> <field eval="False" name="filter"/> <field name="view_id" ref="view_task_tree2"/> <field name="context">{"search_default_user_id":uid, "search_default_current": 1}</field> @@ -450,7 +507,7 @@ <field name="name">Overpassed Tasks</field> <field name="res_model">project.task</field> <field name="view_type">form</field> - <field name="view_mode">tree,form,calendar,graph</field> + <field name="view_mode">tree,form,calendar,graph,kanban</field> <field name="domain">[('date_deadline','<',time.strftime('%Y-%m-%d')),('state','in',('draft','pending','open'))]</field> <field name="filter" eval="True"/> <field name="search_view_id" ref="view_task_search_form"/> @@ -461,7 +518,7 @@ <field name="res_model">project.task</field> <field name="name">Project's tasks</field> <field name="view_type">form</field> - <field name="view_mode">tree,form,calendar,graph,gantt</field> + <field name="view_mode">tree,form,calendar,graph,gantt,kanban</field> <field name="domain">[('project_id', 'child_of', [active_id])]</field> <field name="context">{'project_id':active_id, 'active_test':False}</field> </record> diff --git a/addons/project/wizard/__init__.py b/addons/project/wizard/__init__.py index 69e1051e621025d19243be0c36d9272113c7c8fd..f5324d2b6aae9329c1b678e4afe05121357819aa 100644 --- a/addons/project/wizard/__init__.py +++ b/addons/project/wizard/__init__.py @@ -19,7 +19,7 @@ # ############################################################################## -import project_task_close +import mail_compose_message import project_task_delegate import project_task_reevaluate diff --git a/addons/project/wizard/mail_compose_message.py b/addons/project/wizard/mail_compose_message.py new file mode 100644 index 0000000000000000000000000000000000000000..82d67ea9b924b101c6155f7380d6b8a56799578f --- /dev/null +++ b/addons/project/wizard/mail_compose_message.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +from osv import osv +from osv import fields +from tools.translate import _ + +class mail_compose_message(osv.osv_memory): + _inherit = 'mail.compose.message' + + def get_value(self, cr, uid, model, resource_id, context=None): + ''' + To get values of the resource_id for the model + @param model: Object + @param resource_id: id of a record for which values to be read + + @return: Returns a dictionary + ''' + result = super(mail_compose_message, self).get_value(cr, uid, model, resource_id, context=context) + if model == 'project.task' and resource_id: + task_pool = self.pool.get('project.task') + task_data = task_pool.browse(cr, uid, resource_id, context=context) + partner = task_data.partner_id or task_data.project_id.partner_id + if task_data.project_id.warn_manager and (not task_data.project_id.user_id or task_data.project_id.user_id and not task_data.project_id.user_id.user_email) : + raise osv.except_osv(_('Error'), _("Please specify the Project Manager or email address of Project Manager.")) + elif task_data.project_id.warn_customer and (not partner or not len(partner.address) or (partner and len(partner.address) and not partner.address[0].email)): + raise osv.except_osv(_('Error'), _("Please specify the Customer or email address of Customer.")) + + result.update({'email_from': task_data.user_id and task_data.user_id.user_email or False}) + val = { + 'name': task_data.name, + 'user_id': task_data.user_id.name, + 'task_id': "%d/%d" % (task_data.project_id.id, task_data.id), + 'date_start': task_data.date_start, + 'date': task_data.date_end, + 'state': task_data.state + } + header = (task_data.project_id.warn_header or '') % val + footer = (task_data.project_id.warn_footer or '') % val + description = u'%s\n %s\n %s\n\n \n%s' % (header, task_data.description or '', footer, task_data.user_id and task_data.user_id.signature) + if partner and len(partner.address): + result.update({'email_to': result.get('email_to',False) and result.get('email_to') + ',' + partner.address[0].email}) + result.update({ + 'body_text': description or False, + 'email_to': task_data.project_id.user_id and task_data.project_id.user_id.user_email or False, + 'subject': _("Task '%s' Closed") % task_data.name, + 'model': model, + 'res_id': resource_id, + }) + + return result + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project/wizard/project_task_close.py b/addons/project/wizard/project_task_close.py deleted file mode 100644 index 627864820f6366e58bda1cbba910f5b5f190bb6e..0000000000000000000000000000000000000000 --- a/addons/project/wizard/project_task_close.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -############################################################################## - -from osv import fields, osv -import tools -from tools.translate import _ -import re -class project_task_close(osv.osv_memory): - """ - Close Task - """ - _name = "project.task.close" - _description = "Project Close Task" - _columns = { - 'manager_warn': fields.boolean("Warn Manager", help="Warn Manager by Email"), - 'partner_warn': fields.boolean("Warn Customer", help="Warn Customer by Email"), - 'manager_email': fields.char('Manager Email', size=128, help="Email Address of Project's Manager"), - 'partner_email': fields.char('Customer Email', size=128, help="Email Address of Customer"), - 'description': fields.text('Description'), - } - - def default_get(self, cr, uid, fields, context=None): - """ - This function gets default values - """ - if context is None: - context = {} - record_id = context and context.get('active_id', False) or False - task_pool = self.pool.get('project.task') - - res = super(project_task_close, self).default_get(cr, uid, fields, context=context) - task = task_pool.browse(cr, uid, record_id, context=context) - project = task.project_id - manager = project.user_id or False - partner = task.partner_id or task.project_id.partner_id - - if 'description' in fields: - res.update({'description': task.description or False}) - if 'manager_warn' in fields: - res.update({'manager_warn': project.warn_manager or False}) - if 'partner_warn' in fields: - res.update({'partner_warn': project.warn_customer or False}) - if 'manager_email' in fields: - res.update({'manager_email': manager and manager.user_email or False}) - if partner and len(partner.address) and 'partner_email' in fields: - res.update({'partner_email': partner.address[0].email}) - return res - - def send(self, cr, uid, ids, context=None): - if context is None: - context = {} - - task_pool = self.pool.get('project.task') - task_id = context.get('active_id', False) - if not task_id: - return {} - task = task_pool.browse(cr, uid, task_id, context=context) - for data in self.browse(cr, uid, ids, context=context): - # Send Warn Message by Email to Manager and Customer - if data.manager_warn and not data.manager_email: - raise osv.except_osv(_('Error'), _("Please specify the email address of Project Manager.")) - - elif data.partner_warn and not data.partner_email: - raise osv.except_osv(_('Error'), _("Please specify the email address of Customer.")) - - elif data.manager_warn or data.partner_warn: - project = task.project_id - subject = _("Task '%s' Closed") % task.name - if task.user_id and task.user_id.user_email: - from_adr = task.user_id.user_email - signature = task.user_id.signature - else: - raise osv.except_osv(_('Error'), _("Couldn't send mail because your email address is not configured!")) - val = { - 'name': task.name, - 'user_id': task.user_id.name, - 'task_id': "%d/%d" % (project.id, task.id), - 'date_start': task.date_start, - 'date_end': task.date_end, - 'state': task.state - } - - to_adr = [] - header = footer = '' - try: - header_str = (project.warn_header or '') - footer_str = (project.warn_footer or '') - header = (re.sub(r'\%\W*\(', '%(', header_str)) % val - footer = (re.sub(r'\%\W*\(', '%(', footer_str)) % val - except: - raise osv.except_osv(_('Error'), _("Invlaid automatic variables used in project header or foooter.")) - body = u'%s\n%s\n%s\n\n-- \n%s' % (header, data.description or '', footer, signature) - if data.manager_warn and data.manager_email: - to_adr.append(data.manager_email) - if data.partner_warn and data.partner_email: - to_adr.append(data.partner_email) - mail_id = tools.email_send(from_adr, to_adr, subject, tools.ustr(body), email_bcc=[from_adr]) - if not mail_id: - raise osv.except_osv(_('Error'), _("Couldn't send mail! Check the email ids and smtp configuration settings")) - return {'type': 'ir.actions.act_window_close'} - -project_task_close() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project/wizard/project_task_close_view.xml b/addons/project/wizard/project_task_close_view.xml deleted file mode 100644 index d6bed92824d3e16c728f730329b77809e0f644fd..0000000000000000000000000000000000000000 --- a/addons/project/wizard/project_task_close_view.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - - <record id="view_project_task_close" model="ir.ui.view"> - <field name="name">Send Email</field> - <field name="model">project.task.close</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Send Email"> - <group colspan="4" col="4"> - <field name="manager_warn" invisible="1"/> - <field name="manager_email" widget="email" attrs="{'invisible':[('manager_warn','=',False)], 'required':[('manager_warn','=',True)]}" width="250"/> - <field name="partner_warn" invisible="1"/> - <field name="partner_email" widget="email" attrs="{'invisible':[('partner_warn','=',False)], 'required':[('partner_warn','=',True)]}"/> - <separator string="Warn Message" colspan="4"/> - <field name="description" nolabel="1" colspan="4" width="200" height="150"/> - </group> - <separator string="" colspan="4"/> - <group colspan="2" col="2"> - </group> - <group colspan="2" col="3"> - <button icon="gtk-cancel" special="cancel" string="_Cancel"/> - <button icon="terp-mail-message-new" string="_Send" name="send" type="object"/> - </group> - </form> - </field> - </record> - - <record id="action_project_task_close" model="ir.actions.act_window"> - <field name="name">Send Email</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">project.task.close</field> - <field name="view_type">form</field> - <field name="view_mode">form</field> - <field name="view_id" ref="view_project_task_close"/> - <field name="target">new</field> - </record> - - </data> -</openerp> diff --git a/addons/project_issue/__init__.py b/addons/project_issue/__init__.py index 21efa34f6f4ac40eb752933f8b10f8891163d7a8..f519698a60ceebf81dd7a034af17a9e5695fd28b 100644 --- a/addons/project_issue/__init__.py +++ b/addons/project_issue/__init__.py @@ -22,6 +22,5 @@ import project_issue import report -import wizard # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_issue/board_project_issue_view.xml b/addons/project_issue/board_project_issue_view.xml index 3b6a6bcd0a3fe2af091a0910b6a7886c1258b552..cfe285134a7c66e76cc596ba9f4832c22636e934 100644 --- a/addons/project_issue/board_project_issue_view.xml +++ b/addons/project_issue/board_project_issue_view.xml @@ -123,7 +123,7 @@ <field name="res_model">project.issue</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> - <field name="domain">[('state','not in',('cancel','done')),'|',('assigned_to','=',uid),('user_id','=',uid)]</field> + <field name="domain">[('state','not in',('cancel','done')),('user_id','=',uid)]</field> <field name="view_id" ref="project_issue_board_tree_view"/> </record> <record id="view_my_open_project_issue_graph" model="ir.ui.view"> @@ -142,7 +142,7 @@ <field name="res_model">project.issue.report</field> <field name="view_type">form</field> <field name="view_mode">graph,tree,form,</field> - <field name="domain">[('state','=','open'),'|',('assigned_to','=',uid),('user_id','=',uid)]</field> + <field name="domain">[('state','=','open'),('user_id','=',uid)]</field> <field name="view_id" ref="view_my_open_project_issue_graph"/> </record> <record id="board_my_project_issue_form" model="ir.ui.view"> diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 61a45f01753bf81a825c1c69fc401b2413e02ace..af756d34c5fefb409cf0bf9a2165b151ebd51b25 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -26,7 +26,9 @@ from tools.translate import _ import binascii import time import tools +from crm import wizard +wizard.mail_compose_message.SUPPORTED_MODELS.append('project.issue') class project_issue_version(osv.osv): _name = "project.issue.version" @@ -44,8 +46,8 @@ class project_issue(crm.crm_case, osv.osv): _name = "project.issue" _description = "Project Issue" _order = "priority, create_date desc" - _inherit = ['mailgate.thread'] - + _inherit = ['mail.thread'] + def case_open(self, cr, uid, ids, *args): """ @param self: The object pointer @@ -56,7 +58,7 @@ class project_issue(crm.crm_case, osv.osv): """ res = super(project_issue, self).case_open(cr, uid, ids, *args) - self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'), 'assigned_to' : uid}) + self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id' : uid}) for (id, name) in self.name_get(cr, uid, ids): message = _("Issue '%s' has been opened.") % name self.log(cr, uid, id, message) @@ -156,7 +158,7 @@ class project_issue(crm.crm_case, osv.osv): issues = [] issue_pool = self.pool.get('project.issue') for task in self.pool.get('project.task').browse(cr, uid, ids, context=context): - issues += issue_pool.search(cr, uid, [('task_id','=',task.id)]) + issues += issue_pool.search(cr, uid, [('task_id','=',task.id)]) return issues def _get_issue_work(self, cr, uid, ids, context=None): @@ -174,8 +176,8 @@ class project_issue(crm.crm_case, osv.osv): progress = 0.0 if issue.task_id: progress = task_pool._hours_get(cr, uid, [issue.task_id.id], field_names, args, context=context)[issue.task_id.id]['progress'] - res[issue.id] = {'progress' : progress} - return res + res[issue.id] = {'progress' : progress} + return res _columns = { 'id': fields.integer('ID'), @@ -189,7 +191,6 @@ class project_issue(crm.crm_case, osv.osv): 'section_id': fields.many2one('crm.case.section', 'Sales Team', \ select=True, help='Sales team to which Case belongs to.\ Define Responsible user and Email account for mail gateway.'), - 'user_id': fields.related('project_id', 'user_id', type='many2one', relation='res.users', store=True, select=1, string='Responsible'), 'partner_id': fields.many2one('res.partner', 'Partner', select=1), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \ domain="[('partner_id','=',partner_id)]"), @@ -218,14 +219,14 @@ class project_issue(crm.crm_case, osv.osv): multi='compute_day', type="float", store=True), 'day_close': fields.function(_compute_day, string='Days to Close', \ multi='compute_day', type="float", store=True), - 'assigned_to': fields.many2one('res.users', 'Assigned to', required=False, select=1), + 'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1), 'working_hours_open': fields.function(_compute_day, string='Working Hours to Open the Issue', \ multi='compute_day', type="float", store=True), 'working_hours_close': fields.function(_compute_day, string='Working Hours to Close the Issue', \ multi='compute_day', type="float", store=True), 'inactivity_days': fields.function(_compute_day, string='Days since last action', \ multi='compute_day', type="integer", help="Difference in days between last action and current date"), - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]), 'date_action_last': fields.datetime('Last Action', readonly=1), 'date_action_next': fields.datetime('Next Action', readonly=1), 'progress': fields.function(_hours_get, string='Progress (%)', multi='hours', group_operator="avg", help="Computed as: Time Spent / Total Time.", @@ -243,19 +244,11 @@ class project_issue(crm.crm_case, osv.osv): return False def on_change_project(self, cr, uid, ids, project_id, context=None): - result = {} - - if project_id: - project = self.pool.get('project.project').browse(cr, uid, project_id, context=context) - if project.user_id: - result['value'] = {'user_id' : project.user_id.id} - - return result + return {} _defaults = { 'active': 1, - #'user_id': crm.crm_case._get_default_user, 'partner_id': crm.crm_case._get_default_partner, 'partner_address_id': crm.crm_case._get_default_partner_address, 'email_from': crm.crm_case._get_default_email, @@ -265,7 +258,6 @@ class project_issue(crm.crm_case, osv.osv): 'priority': crm.AVAILABLE_PRIORITIES[2][0], 'project_id':_get_project, 'categ_id' : lambda *a: False, - #'assigned_to' : lambda obj, cr, uid, context: uid, } def convert_issue_task(self, cr, uid, ids, context=None): @@ -294,7 +286,7 @@ class project_issue(crm.crm_case, osv.osv): 'date': bug.date, 'project_id': bug.project_id.id, 'priority': bug.priority, - 'user_id': bug.assigned_to.id, + 'user_id': bug.user_id.id, 'planned_hours': 0.0, }) @@ -362,7 +354,7 @@ class project_issue(crm.crm_case, osv.osv): if not task_id: return {'value':{}} task = self.pool.get('project.task').browse(cr, uid, task_id, context=context) - return {'value':{'assigned_to': task.user_id.id,}} + return {'value':{'user_id': task.user_id.id,}} def case_escalate(self, cr, uid, ids, *args): """Escalates case to top level @@ -384,26 +376,17 @@ class project_issue(crm.crm_case, osv.osv): else: raise osv.except_osv(_('Warning !'), _('You cannot escalate this issue.\nThe relevant Project has not configured the Escalation Project!')) self.write(cr, uid, [case.id], data) - self._history(cr, uid, cases, _('Escalate')) + self.message_append(cr, uid, cases, _('Escalate')) return True - def message_new(self, cr, uid, msg, context=None): - """ - Automatically calls when new email message arrives - - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks - """ - if context is None: + def message_new(self, cr, uid, msg, custom_values=None, context=None): + """Automatically called when new email message arrives""" + if context is None: context = {} - mailgate_pool = self.pool.get('email.server.tools') - subject = msg.get('subject') or _('No Title') - body = msg.get('body') + body = msg.get('body_text') msg_from = msg.get('from') priority = msg.get('priority') - vals = { 'name': subject, 'email_from': msg_from, @@ -411,37 +394,20 @@ class project_issue(crm.crm_case, osv.osv): 'description': body, 'user_id': False, } - if msg.get('priority', False): + if priority: vals['priority'] = priority - - res = mailgate_pool.get_partner(cr, uid, msg.get('from')) - if res: - vals.update(res) + vals.update(self.message_partner_by_email(cr, uid, msg_from)) context.update({'state_to' : 'draft'}) - res = self.create(cr, uid, vals, context=context) - self.convert_to_bug(cr, uid, [res], context=context) - - attachents = msg.get('attachments', []) - for attactment in attachents or []: - data_attach = { - 'name': attactment, - 'datas': binascii.b2a_base64(str(attachents.get(attactment))), - 'datas_fname': attactment, - 'description': 'Mail attachment', - 'res_model': self._name, - 'res_id': res, - } - self.pool.get('ir.attachment').create(cr, uid, data_attach) - return res + if custom_values and isinstance(custom_values, dict): + vals.update(custom_values) - def message_update(self, cr, uid, ids, vals=None, msg="", default_act='pending', context=None): - """ - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of update mail’s IDs - """ + res_id = self.create(cr, uid, vals, context) + self.message_append_dict(cr, uid, [res_id], msg, context=context) + self.convert_to_bug(cr, uid, [res_id], context=context) + return res_id + + def message_update(self, cr, uid, ids, msg, vals=None, default_act='pending', context=None): if vals is None: vals = {} @@ -450,7 +416,7 @@ class project_issue(crm.crm_case, osv.osv): ids = [ids] vals.update({ - 'description': msg['body'] + 'description': msg['body_text'] }) if msg.get('priority', False): vals['priority'] = msg.get('priority') @@ -467,7 +433,7 @@ class project_issue(crm.crm_case, osv.osv): record.write({'state' : 'open'}) vls = { } - for line in msg['body'].split('\n'): + for line in msg['body_text'].split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower(), False): @@ -476,20 +442,9 @@ class project_issue(crm.crm_case, osv.osv): vals.update(vls) res = self.write(cr, uid, ids, vals) + self.message_append_dict(cr, uid, ids, msg, context=context) return res - def msg_send(self, cr, uid, id, *args, **argv): - - """ Send The Message - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of email’s IDs - @param *args: Return Tuple Value - @param **args: Return Dictionary of Keyword Value - """ - return True - def copy(self, cr, uid, id, default=None, context=None): issue = self.read(cr, uid, id, ['name'], context=context) if not default: diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 1154579960ea653b94f0b2af8147c9615721e4da..860dd73216b1e23832e0a602caafa3c1ef568871 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -55,8 +55,7 @@ <field name="name"/> <field name="project_id" required="True" on_change="on_change_project(project_id)"/> <field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'project.issue')]"/> - <field name="user_id" invisible="1" /> - <field name="assigned_to"/> + <field name="user_id"/> <field name="version_id" colspan="2" widget="selection"/> <group colspan="2" col="4"> <field name="type_id" string="Resolution" /> @@ -97,15 +96,15 @@ <group colspan="4"> <field colspan="4" name="email_cc" string="Global CC" widget="url"/> </group> - <field name="message_ids" colspan="4" nolabel="1" mode="tree,form"> + <field name="message_ids" colspan="4" nolabel="1" mode="tree,form" readonly="1"> <tree string="History"> <field name="display_text" string="History Information"/> - <field name="history" invisible="1"/> - <button - string="Reply" attrs="{'invisible': [('history', '!=', True)]}" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'project.issue', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <field name="email_from" invisible="1"/> + <button + string="Reply" attrs="{'invisible': [('email_from', '=', False)]}" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action" /> </tree> <form string="History"> <group col="4" colspan="4"> @@ -113,20 +112,18 @@ <field name="date"/> <field name="email_to" widget="char" size="512"/> <field name="email_cc" widget="char" size="512"/> - <field name="name" colspan="4" widget="char" size="512"/> - <field name="history" invisible="1"/> + <field name="subject" colspan="4" widget="char" size="512"/> </group> <notebook colspan="4"> <page string="Details"> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <field name="description" colspan="4" nolabel="1" height="250"/> - <button colspan="4" - string="Reply" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'reply', 'model': 'project.issue', 'include_original' : True}" - icon="terp-mail-replied" type="action" /> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1" height="250"/> + <button colspan="4" string="Reply" + name="%(mail.action_email_compose_message_wizard)d" + context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" + icon="terp-mail-replied" type="action"/> </group> - <group attrs="{'invisible': [('history', '=', True)]}"> + <group attrs="{'invisible': [('email_from', '!=', False)]}"> <field name="display_text" colspan="4" nolabel="1" height="250"/> </group> </page> @@ -141,10 +138,9 @@ name="%(crm.action_crm_add_note)d" context="{'model': 'crm.lead' }" icon="terp-document-new" type="action" /> - <button string="Send New Email" - name="%(crm.action_crm_send_mail)d" - context="{'mail':'new', 'model': 'project.issue'}" - icon="terp-mail-message-new" type="action" /> + <button string="Send New Email" + name="%(mail.action_email_compose_message_wizard)d" + icon="terp-mail-message-new" type="action"/> </page> <page string="Extra Info" groups="base.group_extended"> <group col="2" colspan="2"> @@ -191,7 +187,7 @@ <button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/> <button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/> <field name="version_id" widget="selection"/> - <field name="assigned_to"/> + <field name="user_id"/> <field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/> <field name="state"/> <button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" icon="gtk-cancel"/> @@ -219,8 +215,8 @@ <filter string="Pending" domain="[('state','=','pending')]" help="Pending Issues" icon="terp-gtk-media-pause"/> <separator orientation="vertical"/> <field name="name" string="Issue / Partner" filter_domain="['|', '|', ('partner_id','ilike',self), ('email_from','ilike',self), ('name','ilike',self)]"/> - <field name="assigned_to"> - <filter domain="[('assigned_to','=',False)]" help="Unassigned Issues" icon="terp-personal-" separator="1"/> + <field name="user_id"> + <filter domain="[('user_id','=',False)]" help="Unassigned Issues" icon="terp-personal-" separator="1"/> </field> <field name="project_id"/> <field name="id"/> @@ -228,7 +224,7 @@ <newline/> <group expand="0" string="Group By..." groups="base.group_extended"> <filter string="Responsible" icon="terp-personal" - domain="[]" context="{'group_by':'assigned_to'}" /> + domain="[]" context="{'group_by':'user_id'}" /> <filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}" /> <separator orientation="vertical"/> @@ -259,7 +255,7 @@ <field name="type">calendar</field> <field name="priority" eval="2"/> <field name="arch" type="xml"> - <calendar string="Issues" date_start="date" color="assigned_to" date_delay="duration"> + <calendar string="Issues" date_start="date" color="user_id" date_delay="duration"> <field name="name"/> <field name="partner_id"/> </calendar> @@ -284,7 +280,7 @@ <button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/> <button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/> <field name="version_id"/> - <field name="assigned_to"/> + <field name="user_id"/> <field name="state"/> <button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" icon="gtk-cancel"/> <button name="case_close" string="Done" states="open,draft,pending" type="object" icon="gtk-jump-to"/> @@ -307,7 +303,7 @@ <separator orientation="vertical"/> <group> <field name="name" select='1' string="Feature description"/> - <field name="assigned_to" select="1"/> + <field name="user_id" select="1"/> <field name="state" select="1"> <filter icon="terp-check" domain="[('state','in',('open','draft'))]" help="Current Features" name="current_feature"/> <filter icon="terp-camera_test" domain="[('state','=','open')]" help="Open Features"/> diff --git a/addons/project_issue/report/project_issue_report.py b/addons/project_issue/report/project_issue_report.py index 5eae252f44692e235b3c3c2d5fd674f3eff7da19..36ce72f2a70ce71cce154552e675bd8d25a8c9ec 100644 --- a/addons/project_issue/report/project_issue_report.py +++ b/addons/project_issue/report/project_issue_report.py @@ -37,7 +37,6 @@ class project_issue_report(osv.osv): _columns = { 'name': fields.char('Year', size=64, required=False, readonly=True), - 'user_id':fields.many2one('res.users', 'Responsible', readonly=True), 'section_id':fields.many2one('crm.case.section', 'Sale Team', readonly=True), 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True), 'month':fields.selection([('01', 'January'), ('02', 'February'), \ @@ -64,7 +63,7 @@ class project_issue_report(osv.osv): 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), 'project_id':fields.many2one('project.project', 'Project',readonly=True), 'version_id': fields.many2one('project.issue.version', 'Version'), - 'assigned_to' : fields.many2one('res.users', 'Assigned to',readonly=True), + 'user_id' : fields.many2one('res.users', 'Assigned to',readonly=True), 'partner_id': fields.many2one('res.partner','Partner',domain="[('object_id.model', '=', 'project.issue')]"), 'channel_id': fields.many2one('crm.case.channel', 'Channel',readonly=True), 'task_id': fields.many2one('project.task', 'Task',domain="[('object_id.model', '=', 'project.issue')]" ), @@ -95,14 +94,14 @@ class project_issue_report(osv.osv): c.project_id as project_id, c.version_id as version_id, 1 as nbr, - c.assigned_to, c.partner_id, c.channel_id, c.task_id, date_trunc('day',c.create_date) as create_date, extract('epoch' from (c.date_open-c.create_date))/(3600*24) as delay_open, extract('epoch' from (c.date_closed-c.date_open))/(3600*24) as delay_close, - (SELECT count(id) FROM mailgate_message WHERE model='project.issue' AND res_id=c.id) AS email + (SELECT count(id) FROM mail_message WHERE model='project.issue' AND res_id=c.id) AS email + FROM project_issue c WHERE c.categ_id IN (select id from crm_case_categ where object_id in (select id from ir_model where model = 'project.issue')) diff --git a/addons/project_issue/report/project_issue_report_view.xml b/addons/project_issue/report/project_issue_report_view.xml index 4808ff29f81eb0bb1eafcfae250423360d425295..1ed21b8dc01e4940b61ce52f1b5be0e20f311636 100644 --- a/addons/project_issue/report/project_issue_report_view.xml +++ b/addons/project_issue/report/project_issue_report_view.xml @@ -20,8 +20,7 @@ <field name="categ_id" invisible="1"/> <field name="channel_id" invisible="1"/> <field name="partner_id" invisible="1"/> - <field name="task_id" invisible="1"/> - <field name="assigned_to" invisible="1"/> + <field name="task_id" invisible="1"/> <field name="date_closed" invisible="1"/> <field name="state" invisible="1"/> <field name="day" invisible="1"/> @@ -89,31 +88,14 @@ </field> <field name="project_id"/> - <field name="user_id" select="1" widget="selection"/> + <field name="user_id" select="1" widget="selection" filter_domain="[('user_id','ilike',self)]"/> + <field name="partner_id"/> + <field name="version_id" widget="selection" /> </group> <newline/> - <group expand="0" string="Extended Filters..." colspan="10" col="12" groups="base.group_extended"> - <field name="partner_id"/> - <field name="assigned_to" widget="selection"/> - <separator orientation="vertical"/> - <field name="priority" /> - <separator orientation="vertical"/> - <field name="version_id" widget="selection" /> - <field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'project.issue')]"/> - <field name="type_id" widget="selection"/> - <separator orientation="vertical"/> - <field name="company_id" widget="selection" groups="base.group_multi_company"/> - <newline/> - <field name="creation_date"/> - <field name="opening_date"/> - <field name="date_closed" string="Date Closed"/> - </group> - <newline/> - <group expand="1" string="Group By..." colspan="4" col="8"> - <filter name="User" string="Responsible" icon="terp-personal" - domain="[]" context="{'group_by':'user_id'}" /> + <group expand="1" string="Group By..." colspan="4" col="8"> <filter string="Assigned to" name="Responsible" icon="terp-personal" - domain="[]" context="{'group_by':'assigned_to'}" /> + domain="[]" context="{'group_by':'user_id'}" /> <filter string="Partner" icon="terp-partner" context="{'group_by':'partner_id'}" /> <separator orientation="vertical" /> <filter string="Sale Team" icon="terp-personal+" diff --git a/addons/project_issue/security/ir.model.access.csv b/addons/project_issue/security/ir.model.access.csv index 177533462a0b14b2abb8fcef62d4039a9b5ce355..01afcb73b0986485b8da0a90a5b1478b26d783df 100644 --- a/addons/project_issue/security/ir.model.access.csv +++ b/addons/project_issue/security/ir.model.access.csv @@ -5,7 +5,7 @@ "access_crm_case_categ_id","crm.case.categ","crm.model_crm_case_categ","project.group_project_manager",1,1,1,1 "access_project_issue_version_project","project_issue_version manager","model_project_issue_version","project.group_project_manager",1,1,1,1 "access_project_issue_version_project_user","project_issue_version user","model_project_issue_version","project.group_project_user",1,0,0,0 -"access_mailgate_message_project_manager","mailgate.message.manager","mail_gateway.model_mailgate_message","project.group_project_manager",1,1,1,1 +"access_mail_message_project_manager","mail.message.manager","mail.model_mail_message","project.group_project_manager",1,1,1,1 "access_resource_calendar_project_manager","resource.calendar.project.manager","resource.model_resource_calendar","project.group_project_manager",1,1,1,1 "access_project_issue_report_user","project.issue.report user","model_project_issue_report","project.group_project_user",1,0,0,0 -"access_mailgate_message_issue_project_user","project.mailgate.message.issue.user","mail_gateway.model_mailgate_message","project.group_project_user",1,1,1,0 +"access_mail_message_issue_project_user","mail.message.user","mail.model_mail_message","project.group_project_user",1,1,1,0 diff --git a/addons/project_issue_sheet/project_issue_sheet_view.xml b/addons/project_issue_sheet/project_issue_sheet_view.xml index 058fd67a946997038d2577ad2215c4f39644c63c..1e9b139aed1e99d4e3060ec3c36af739119cbb1c 100644 --- a/addons/project_issue_sheet/project_issue_sheet_view.xml +++ b/addons/project_issue_sheet/project_issue_sheet_view.xml @@ -18,7 +18,7 @@ </xpath> <notebook colspan="4"> <page string="Worklogs"> - <field name="timesheet_ids" colspan="4" nolabel="1" context="{'default_user_id' : assigned_to, 'default_account_id' : analytic_account_id}"> + <field name="timesheet_ids" colspan="4" nolabel="1" context="{'default_user_id' : user_id, 'default_account_id' : analytic_account_id}"> <tree editable="top" string="Timesheet"> <field name="name"/> <field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" widget="float_time"/> diff --git a/addons/project_mailgate/__openerp__.py b/addons/project_mailgate/__openerp__.py index 4551d19bb1f8b63c66becae8a9426c3b91688223..76ff6dbbc4703f7c966119d8a4e876e3a3e00dc2 100644 --- a/addons/project_mailgate/__openerp__.py +++ b/addons/project_mailgate/__openerp__.py @@ -21,20 +21,28 @@ { - "name": "Project MailGateWay", + "name": "Project Tasks - Mail Integration", "version": "1.1", "author": "OpenERP SA", "website": "http://www.openerp.com", "category": "Project Management", 'complexity': "easy", "images": ["images/project_mailgate_task.jpeg"], - "depends": ["project", "mail_gateway"], + "depends": ["project", "mail"], "description": """ -This module is an interface that synchronises mails with OpenERP Project Task. -============================================================================== +This module can automatically create Project Tasks based on incoming emails +=========================================================================== + +Allows creating tasks based on new emails arriving at a given mailbox, +similarly to what the CRM application has for Leads/Opportunities. +There are two common alternatives to configure the mailbox integration: + + * Install the ``fetchmail`` module and configure a new mailbox, then select + ``Project Tasks`` as the target for incoming emails. + * Set it up manually on your mail server based on the 'mail gateway' script + provided in the ``mail`` module - and connect it to the `project.task` model. + -It allows creating tasks as soon as a new mail arrives in our configured mail server. -Moreover, it keeps track of all further communications and task states. """, "init_xml": [], "update_xml": ["security/ir.model.access.csv", diff --git a/addons/project_mailgate/project_mailgate.py b/addons/project_mailgate/project_mailgate.py index 2325d533c03f69aa7b26a654057f27003ad2db77..9715a32c151c3b19c2ab207d355ee106359780a6 100644 --- a/addons/project_mailgate/project_mailgate.py +++ b/addons/project_mailgate/project_mailgate.py @@ -21,121 +21,85 @@ from osv import fields, osv from tools.translate import _ +import tools import binascii class project_tasks(osv.osv): _name = "project.task" - _inherit = ['mailgate.thread', 'project.task'] + _inherit = ['mail.thread','project.task'] - _columns={ - 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model', '=', _name)], readonly=True), - } + _columns = { + 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)], readonly=True), + } - def message_new(self, cr, uid, msg, context=None): -# """ -# Automatically calls when new email message arrives -# -# @param self: The object pointer -# @param cr: the current row, from the database cursor, -# @param uid: the current user’s ID for security checks -# """ - mailgate_obj = self.pool.get('email.server.tools') + def message_new(self, cr, uid, msg, custom_values=None, context=None): + res_id = super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) subject = msg.get('subject') - body = msg.get('body') + body = msg.get('body_text') msg_from = msg.get('from') - #TODO map email priority with openerp task priority - priority = msg.get('priority') - data = { 'name': subject, 'description': body, 'planned_hours': 0.0, } - res = mailgate_obj.get_partner(cr, uid, msg_from) - if res: - data.update(res) - res = self.create(cr, uid, data) - - attachments = msg.get('attachments', []) - for attachment in attachments or []: - data_attach = { - 'name': attachment, - 'datas': binascii.b2a_base64(str(attachments.get(attachment))), - 'datas_fname': attachment, - 'description': 'Mail attachment', - 'res_model': self._name, - 'res_id': res, - } - self.pool.get('ir.attachment').create(cr, uid, data_attach) + data.update(self.message_partner_by_email(cr, uid, msg_from)) + self.write(cr, uid, [res_id], data, context) + return res_id - return res - - def message_update(self, cr, uid, id, msg, data={}, default_act='pending'): - mailgate_obj = self.pool.get('email.server.tools') - msg_actions, body_data = mailgate_obj.msg_act_get(msg) + def message_update(self, cr, uid, ids, msg, data={}, default_act='pending'): data.update({ - 'description': body_data, + 'description': msg['body_text'], }) act = 'do_'+default_act - if 'state' in msg_actions: - if msg_actions['state'] in ['draft', 'close', 'cancel', 'open', 'pending']: - act = 'do_' + msg_actions['state'] - - for k1, k2 in [('cost', 'planned_hours')]: - try: - data[k2] = float(msg_actions[k1]) - except: - pass - if 'priority' in msg_actions: - if msg_actions['priority'] in ('1', '2', '3', '4', '5'): - data['priority'] = msg_actions['priority'] - - self.write(cr, uid, [id], data) - getattr(self, act)(cr, uid, [id]) - return True - - def message_followers(self, cr, uid, ids, context=None): - res = [] - if isinstance(ids, (str, int, long)): - select = [ids] - else: - select = ids - for task in self.browse(cr, uid, select, context=context): - user_email = (task.user_id and task.user_id.user_email) or False - res += [(user_email, False, False, task.priority)] - if isinstance(ids, (str, int, long)): - return len(res) and res[0] or False - return res - - def msg_send(self, cr, uid, id, *args, **argv): + maps = { + 'cost':'planned_hours', + } + for line in msg['body_text'].split('\n'): + line = line.strip() + res = tools.misc.command_re.match(line) + if res: + match = res.group(1).lower() + field = maps.get(match) + if field: + try: + data[field] = float(res.group(2).lower()) + except (ValueError, TypeError): + pass + elif match.lower() == 'state' \ + and res.group(2).lower() in ['cancel','close','draft','open','pending']: + act = 'do_%s' % res.group(2).lower() + + self.write(cr, uid, ids, data, context=context) + getattr(self,act)(cr, uid, ids, context=context) + self.message_append_dict(cr, uid, [res_id], msg, context=context) return True - def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=[], context=None): - mailgate_pool = self.pool.get('mailgate.thread') - return mailgate_pool.history(cr, uid, cases, keyword, history=history,\ - subject=subject, email=email, \ - details=details, email_from=email_from,\ - message_id=message_id, attach=attach, \ - context=context) + def message_thread_followers(self, cr, uid, ids, context=None): + followers = super(project_tasks,self).message_thread_followers(cr, uid, ids, context=context) + for task in self.browse(cr, uid, followers.keys(), context=context): + task_followers = set(followers[task.id]) + task_followers.add(task.user_id.user_email) + followers[task.id] = filter(None, task_followers) + return followers def do_draft(self, cr, uid, ids, context=None): res = super(project_tasks, self).do_draft(cr, uid, ids, context) tasks = self.browse(cr, uid, ids, context=context) - self._history(cr, uid, tasks, _('Draft'), context=context) + self.message_append(cr, uid, tasks, _('Draft'), context=context) return res def do_open(self, cr, uid, ids, context=None): res = super(project_tasks, self).do_open(cr, uid, ids, context) tasks = self.browse(cr, uid, ids, context=context) - self._history(cr, uid, tasks, _('Open'), context=context) + self.message_append(cr, uid, tasks, _('Open'), context=context) return res def do_pending(self, cr, uid, ids, context=None): res = super(project_tasks, self).do_pending(cr, uid, ids, context) tasks = self.browse(cr, uid, ids, context=context) - self._history(cr, uid, tasks, _('Pending'), context=context) + self.message_append(cr, uid, tasks, _('Pending'), context=context) return res def do_close(self, cr, uid, ids, context=None): @@ -143,13 +107,13 @@ class project_tasks(osv.osv): tasks = self.browse(cr, uid, ids, context=context) for task in tasks: if task.state == 'done': - self._history(cr, uid, tasks, _('Done'), context=context) + self.message_append(cr, uid, tasks, _('Done'), context=context) return res def do_cancel(self, cr, uid, ids, context=None): res = super(project_tasks, self).do_cancel(cr, uid, ids, context=context) tasks = self.browse(cr, uid, ids, context=context) - self._history(cr, uid, tasks, _('Cancel'), context=context) + self.message_append(cr, uid, tasks, _('Cancel'), context=context) return res project_tasks() diff --git a/addons/project_mailgate/project_mailgate_view.xml b/addons/project_mailgate/project_mailgate_view.xml index 8c8816ebbd5ab63e0ec9a8c45cb1b92999af552a..c3cd4bf9db2e1bdefdb9817fe93eb1a6a117aca4 100644 --- a/addons/project_mailgate/project_mailgate_view.xml +++ b/addons/project_mailgate/project_mailgate_view.xml @@ -9,7 +9,7 @@ <field name="arch" type="xml"> <xpath expr="/form/notebook/page[@string='Extra Info']" position="before"> <page string="History"> - <field name="message_ids" colspan="4" nolabel="1" mode="tree,form"> + <field name="message_ids" colspan="4" nolabel="1" mode="tree,form" readonly="1"> <tree string="History"> <field name="display_text" string="History Information"/> </tree> @@ -19,15 +19,14 @@ <field name="date"/> <field name="email_to" widget="char" size="512"/> <field name="email_cc" widget="char" size="512"/> - <field name="name" colspan="4" widget="char" size="512"/> - <field name="history" invisible="1"/> + <field name="subject" colspan="4" widget="char" size="512"/> </group> <notebook colspan="4"> <page string="Details"> - <group attrs="{'invisible': [('history', '!=', True)]}"> - <field name="description" colspan="4" nolabel="1" height="250"/> + <group attrs="{'invisible': [('email_from', '=', False)]}"> + <field name="body_text" colspan="4" nolabel="1" height="250"/> </group> - <group attrs="{'invisible': [('history', '=', True)]}"> + <group attrs="{'invisible': [('email_from', '!=', False)]}"> <field name="display_text" colspan="4" nolabel="1" height="250"/> </group> </page> diff --git a/addons/project_mailgate/security/ir.model.access.csv b/addons/project_mailgate/security/ir.model.access.csv index 1a840d89e9a7ad87725cbbd242cbf45bad510813..cf9a08b958de32077e484db9ba6c018d06268af8 100644 --- a/addons/project_mailgate/security/ir.model.access.csv +++ b/addons/project_mailgate/security/ir.model.access.csv @@ -1,3 +1,3 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_mailgate_message_project_manager","project.mailgate.message.manager","mail_gateway.model_mailgate_message","project.group_project_manager",1,1,1,0 -"access_mailgate_message_project_user","project.mailgate.message.user","mail_gateway.model_mailgate_message","project.group_project_user",1,1,1,0 +"access_mail_message_project_manager","project.mail.message.manager","mail.model_mail_message","project.group_project_manager",1,1,1,0 +"access_mail_message_project_user","project.mail.message.user","mail.model_mail_message","project.group_project_user",1,1,1,0 diff --git a/addons/project_planning/project_planning_view.xml b/addons/project_planning/project_planning_view.xml index ef39b5daad9c876de5fb631ef1704d1908778e96..016a10d940fa004214be4425c2a85f625b567cbe 100644 --- a/addons/project_planning/project_planning_view.xml +++ b/addons/project_planning/project_planning_view.xml @@ -109,7 +109,7 @@ <button name="do_reopen" states="done,cancelled" string="Reactivate" type="object" icon="gtk-convert"/> <button name="do_pending" states="open" string="Pending" type="object" icon="gtk-media-pause"/> <button groups="base.group_extended" name="%(project.action_project_task_delegate)d" states="pending,open" string="Delegate" type="action" icon="gtk-sort-descending"/> - <button name="%(project.action_project_task_close)d" states="pending,open" string="Done" type="action" icon="gtk-jump-to"/> + <button name="%(mail.action_email_compose_message_wizard)d" states="pending,open" string="Done" type="action" icon="gtk-jump-to"/> </group> </page> <page groups="base.group_extended" string="Extra Info" attrs="{'readonly':[('state','=','done')]}"> diff --git a/addons/project_scrum/__openerp__.py b/addons/project_scrum/__openerp__.py index 9a1713b9014920d22cb244904bd5549fb4ad6484..9b2f4c4e046b2a4cee8e791074d79a88b6257804 100644 --- a/addons/project_scrum/__openerp__.py +++ b/addons/project_scrum/__openerp__.py @@ -50,7 +50,7 @@ More information on the methodology: """, 'author': 'OpenERP SA', 'images': ['images/product_backlogs.jpeg', 'images/project_sprints.jpeg', 'images/scrum_dashboard.jpeg', 'images/scrum_meetings.jpeg'], - 'depends': ['project', 'process'], + 'depends': ['project', 'process', 'mail'], 'init_xml': [], 'update_xml': [ 'security/ir.model.access.csv', @@ -58,7 +58,7 @@ More information on the methodology: 'wizard/project_scrum_backlog_create_task_view.xml', 'wizard/project_scrum_backlog_merger_view.xml', 'wizard/project_scrum_postpone_view.xml', - "wizard/project_scrum_email_view.xml", +# "wizard/project_scrum_email_view.xml", 'project_scrum_view.xml', 'wizard/project_scrum_backlog_sprint_view.xml', 'process/project_scrum_process.xml', diff --git a/addons/project_scrum/project_scrum.py b/addons/project_scrum/project_scrum.py index 334a415982df7e99358d1927bfc8575f054c1f3a..f0db98294e77c4352eb05ae6ddc5a68b19602a13 100644 --- a/addons/project_scrum/project_scrum.py +++ b/addons/project_scrum/project_scrum.py @@ -317,6 +317,7 @@ class project_scrum_meeting(osv.osv): return True def email_send(self, cr, uid, ids, email, context=None): + mail_message_obj = self.pool.get('mail.message') meeting_id = self.browse(cr, uid, ids, context=context)[0] user = self.pool.get('res.users').browse(cr, uid, uid, context=context) user_email = user.user_email or tools.config.get('email_from', False) @@ -324,7 +325,7 @@ class project_scrum_meeting(osv.osv): body += _('\n* Tasks since yesterday:\n_______________________\n%s\n\n* Task for Today:\n_______________________ \n%s\n\n* Blocks encountered:\n_______________________ \n\n%s') %(meeting_id.question_yesterday,meeting_id.question_today, meeting_id.question_blocks or _('No Blocks')) body += _("\n\nThank you,\n%s") % user.name sub_name = meeting_id.name or _('Scrum Meeting of %s') % meeting_id.date - flag = tools.email_send(user_email , [email], sub_name, body, reply_to=None, openobject_id=str(meeting_id.id)) + flag = mail_message_obj.schedule_with_attach(cr, uid, user_email , [email], sub_name, body, model='project.scrum.meeting', res_id=meeting_id.id, context=context) if not flag: return False return True diff --git a/addons/project_scrum/project_scrum_view.xml b/addons/project_scrum/project_scrum_view.xml index 18421b784c3bdd0c400327d9aab97701e9904214..19d1bcc6b84d9929fe2a354edeee4ef387492325 100644 --- a/addons/project_scrum/project_scrum_view.xml +++ b/addons/project_scrum/project_scrum_view.xml @@ -302,7 +302,7 @@ </notebook> <group col="8" colspan="4"> <field name="state" readonly="1"/> - <button name="%(project_scrum.report_scrum_sprint_burndown_chart)d" + <button name="%(project_scrum.report_scrum_sprint_burndown_chart)d" string="Burndown Chart" type="action" icon="gtk-print"/> <button type="object" string="Open" name="button_open" states="draft,pending" icon="terp-camera_test"/> <button type="object" string="Pending" name="button_pending" states="open" icon="gtk-media-pause"/> @@ -396,7 +396,9 @@ <field name="date"/> <field name="sprint_id" domain="[('state', '=', 'open')]"/> <field name="user_id"/> - <button name="%(action_project_scrum_email)d" string="Send Email" type="action" icon="terp-mail-message-new" /> + <button name="%(mail.action_email_compose_message_wizard)d" + string="Send Email" type="action" icon="terp-mail-message-new" + /> </group> <notebook colspan="4"> <page string="Scrum Meeting"> diff --git a/addons/project_scrum/wizard/__init__.py b/addons/project_scrum/wizard/__init__.py index 4b5b0919574960e9a8e8b51d9ae4c30ae4bfc5c2..fcba1742d7563612cce9a2adb107707e886cce07 100644 --- a/addons/project_scrum/wizard/__init__.py +++ b/addons/project_scrum/wizard/__init__.py @@ -23,7 +23,7 @@ import project_scrum_backlog_create_task import project_scrum_backlog_sprint import project_scrum_backlog_merger import project_scrum_postpone -import project_scrum_email +import mail_compose_message # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_scrum/wizard/mail_compose_message.py b/addons/project_scrum/wizard/mail_compose_message.py new file mode 100644 index 0000000000000000000000000000000000000000..777ca03fa37057f533ece44822266c653e708b4b --- /dev/null +++ b/addons/project_scrum/wizard/mail_compose_message.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +from osv import osv +from osv import fields +from tools.translate import _ + +class mail_compose_message(osv.osv_memory): + _inherit = 'mail.compose.message' + + def get_value(self, cr, uid, model, resource_id, context=None): + ''' + To get values of the resource_id for the model + @param model: Object + @param resource_id: id of a record for which values to be read + + @return: Returns a dictionary + ''' + if context is None: + context = {} + result = super(mail_compose_message, self).get_value(cr, uid, model, resource_id, context=context) + if model == 'project.scrum.meeting' and resource_id: + meeting_pool = self.pool.get('project.scrum.meeting') + user_pool = self.pool.get('res.users') + meeting = meeting_pool.browse(cr, uid, resource_id, context=context) + + sprint = meeting.sprint_id + user_data = user_pool.browse(cr, uid, uid, context=context) + result.update({'email_from': user_data.address_id and user_data.address_id.email or False}) + + if sprint.scrum_master_id and sprint.scrum_master_id.user_email: + result.update({'email_to': sprint.scrum_master_id.user_email}) + if sprint.product_owner_id and sprint.product_owner_id.user_email: + result.update({'email_to': result.get('email_to',False) and result.get('email_to') + ',' + sprint.product_owner_id.user_email or sprint.product_owner_id.user_email}) + + subject = _("Scrum Meeting : %s") %(meeting.date) + message = _("Hello , \nI am sending you Scrum Meeting : %s for the Sprint '%s' of Project '%s'") %(meeting.date, sprint.name, sprint.project_id.name) + result.update({ + 'subject': subject, + 'body_text': message, + 'model': model, + 'res_id': resource_id + }) + return result + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_scrum/wizard/project_scrum_email.py b/addons/project_scrum/wizard/project_scrum_email.py index f2e8dd79446d7faa4497c40128849f16751093d1..f1d5b6665f7051d61e9a1a62d4995e2a1715b658 100644 --- a/addons/project_scrum/wizard/project_scrum_email.py +++ b/addons/project_scrum/wizard/project_scrum_email.py @@ -69,6 +69,7 @@ class project_scrum_email(osv.osv_memory): if context is None: context = {} + mail_message = self.pool.get('mail.message') active_id = context.get('active_id', False) scrum_meeting_pool = self.pool.get('project.scrum.meeting') user_pool = self.pool.get('res.users') @@ -91,7 +92,7 @@ class project_scrum_email(osv.osv_memory): body += "\n%s\n" %_("Task for Today") body += "_______________________ \n" body += "\n%s\n" %(meeting.question_today or _('None')) - body += "\n%s\n" % _('Blocking points encountered:') + body += "\n%s\n" % _('Blocking points encountered:') body += "_______________________ \n" body += "\n%s\n" %(meeting.question_blocks or _('None')) body += "\n%s\n%s" %(_('Thank you,'), user.name) @@ -100,8 +101,7 @@ class project_scrum_email(osv.osv_memory): if data.scrum_master_email == data.product_owner_email: data.product_owner_email = False if data.scrum_master_email: - tools.email_send(user_email, [data.scrum_master_email], data.subject, body, reply_to=user_email) + mail_message.schedule_with_attach(cr, uid, user_email, [data.scrum_master_email], data.subject, body, reply_to=user_email, context=context) if data.product_owner_email: - tools.email_send(user_email, [data.product_owner_email], data.subject, body, reply_to=user_email) + mail_message.schedule_with_attach(cr, uid, user_email, [data.product_owner_email], data.subject, body, reply_to=user_email, context=context) return {'type': 'ir.actions.act_window_close'} -project_scrum_email() diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index c756bf05128f9e9d5c296b686610494f2bbca330..2c999cdabe9bdede52e32139ba867a7817731067 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -188,7 +188,7 @@ class purchase_order(osv.osv): 'shipped_rate': fields.function(_shipped_rate, string='Received', type='float'), 'invoiced': fields.function(_invoiced, string='Invoiced & Paid', type='boolean', help="It indicates that an invoice has been paid"), 'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'), - 'invoice_method': fields.selection([('manual','Manual'),('order','From Order'),('picking','From Reception')], 'Invoicing Control', required=True, + 'invoice_method': fields.selection([('manual','From PO/PO lines'),('order','Draft invoices pre-generated'),('picking','From receptions')], 'Invoicing Control', required=True, help="From Order: a draft invoice will be generated based on the purchase order. The accountant " \ "will just have to validate this invoice for control.\n" \ "From Reception: a draft invoice will be generated based on validated receptions.\n" \ @@ -612,6 +612,14 @@ class purchase_order_line(osv.osv): res[line.id] = cur_obj.round(cr, uid, cur, taxes['total']) return res + def _get_uom_id(self, cr, uid, context=None): + try: + proxy = self.pool.get('ir.model.data') + result = proxy.get_object_reference(cr, uid, 'product', 'product_uom_unit') + return result[1] + except Exception, ex: + return False + _columns = { 'name': fields.char('Description', size=256, required=True), 'product_qty': fields.float('Quantity', required=True, digits=(16,2)), @@ -639,6 +647,7 @@ class purchase_order_line(osv.osv): } _defaults = { + 'product_uom' : _get_uom_id, 'product_qty': lambda *a: 1.0, 'state': lambda *args: 'draft', 'invoiced': lambda *a: 0, diff --git a/addons/purchase/stock_view.xml b/addons/purchase/stock_view.xml index e02aa4dbf668b8906d532effd7454151f4559a49..538d0b81ab78ad4e23896a11647f834830bcfaea 100644 --- a/addons/purchase/stock_view.xml +++ b/addons/purchase/stock_view.xml @@ -70,11 +70,11 @@ domain="[('state','=','assigned')]" help="Incoming Shipments Available"/> <filter icon="terp-dialog-close" name="done" string="Done" domain="[('state','=','done')]" help="Incoming Shipments already processed"/> - <filter string="To invoice" name="to_invoice" icon="terp-dolar" - domain="[('invoice_state', '=', '2binvoiced')]"/> <separator orientation="vertical"/> <filter icon="terp-accessories-archiver-minus" string="Back Orders" domain="[('backorder_id', '!=', False)]" help="Is a Back Order" groups="base.group_extended"/> + <filter string="To invoice" name="to_invoice" icon="terp-dolar" + domain="[('invoice_state', '=', '2binvoiced')]"/> <separator orientation="vertical"/> <field name="name"/> <field name="partner_id"/> @@ -103,13 +103,13 @@ </record> <record id="action_picking_tree4_picking_to_invoice" model="ir.actions.act_window"> - <field name="name">Pickings to Invoice</field> + <field name="name">On Incoming Shipments</field> <field name="res_model">stock.picking</field> <field name="type">ir.actions.act_window</field> <field name="view_type">form</field> <field name="view_mode">tree,form,calendar</field> <field name="domain">[('type','=','in')]</field> - <field name="context">{'search_default_done':1,'search_default_to_invoice':1}</field> + <field name="context">{'contact_display': 'partner_address',"search_default_available":1}</field> <field name="search_view_id" ref="view_picking_in_search_picking_to_invoice"/> <field name="help">Create invoice from reception of products. If you selected this invoice control method in the purchase order, all receptions done are available here to be invoiced.</field> </record> diff --git a/addons/purchase/wizard/purchase_line_invoice.py b/addons/purchase/wizard/purchase_line_invoice.py index d13d2fac66b30ab9270beebf2e66856646081d42..236cde7dde50c51d8eab9b32aaed68be8fdc9eb6 100644 --- a/addons/purchase/wizard/purchase_line_invoice.py +++ b/addons/purchase/wizard/purchase_line_invoice.py @@ -55,18 +55,6 @@ class purchase_line_invoice(osv.osv_memory): invoice_line_obj=self.pool.get('account.invoice.line') account_jrnl_obj=self.pool.get('account.journal') - def multiple_order_invoice_name(orders): - name = "PO"; - for order in orders: - name += "-%d" % order.id - return name - - def multiple_order_invoice_reference(partner, orders): - reference = "P%dPO" % partner.id - for order in orders: - reference += "-%d" % order.id - return reference - def multiple_order_invoice_notes(orders): notes = "" for order in orders: @@ -82,6 +70,7 @@ class purchase_line_invoice(osv.osv_memory): @param orders : The set of orders to add in the invoice @param lines : The list of line's id """ + name = orders and orders[0].name or '' journal_id = account_jrnl_obj.search(cr, uid, [('type', '=', 'purchase')], context=None) journal_id = journal_id and journal_id[0] or False a = partner.property_account_payable.id @@ -90,11 +79,11 @@ class purchase_line_invoice(osv.osv_memory): else: pay_term = False inv = { - 'name': multiple_order_invoice_name(orders), - 'origin': multiple_order_invoice_name(orders), + 'name': name, + 'origin': name, 'type': 'in_invoice', 'journal_id':journal_id, - 'reference': multiple_order_invoice_reference(partner, orders), + 'reference' : partner.ref, 'account_id': a, 'partner_id': partner.id, 'address_invoice_id': orders[0].partner_address_id.id, diff --git a/addons/sale/sale.py b/addons/sale/sale.py index a37c9331c71f4b8a78ecef9de865952b2cebd077..8fb259b1d0fb98d9d26189efcf2001f6f2376b62 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -838,6 +838,14 @@ class sale_order_line(osv.osv): res[line.id] = 1 return res + def _get_uom_id(self, cr, uid, *args): + try: + proxy = self.pool.get('ir.model.data') + result = proxy.get_object_reference(cr, uid, 'product', 'product_uom_unit') + return result[1] + except Exception, ex: + return False + _name = 'sale.order.line' _description = 'Sales Order Line' _columns = { @@ -877,6 +885,7 @@ class sale_order_line(osv.osv): } _order = 'sequence, id' _defaults = { + 'product_uom' : _get_uom_id, 'discount': 0.0, 'delay': 0.0, 'product_uom_qty': 1, diff --git a/addons/sale/stock.py b/addons/sale/stock.py index 04a9b12687e1c8a0b965ef6608bcc2bdb7f251c9..eeb37c28d9ab0ffd68876e2f0786f272b750d3bf 100644 --- a/addons/sale/stock.py +++ b/addons/sale/stock.py @@ -116,9 +116,13 @@ class stock_picking(osv.osv): def action_invoice_create(self, cursor, user, ids, journal_id=False, group=False, type='out_invoice', context=None): + if context is None: + context = {} invoice_obj = self.pool.get('account.invoice') picking_obj = self.pool.get('stock.picking') invoice_line_obj = self.pool.get('account.invoice.line') + fiscal_position_obj = self.pool.get('account.fiscal.position') + order_line_obj = self.pool.get('sale.order.line') result = super(stock_picking, self).action_invoice_create(cursor, user, ids, journal_id=journal_id, group=group, type=type, @@ -144,6 +148,8 @@ class stock_picking(osv.osv): invoice_obj.write(cursor, user, [invoice_created.id], {'name': inv_name}, context=context) for sale_line in sale_lines: if sale_line.product_id.type == 'service' and sale_line.invoiced == False: + if not type: + type = context.get('inv_type', False) if group: name = picking.name + '-' + sale_line.name else: @@ -168,7 +174,7 @@ class stock_picking(osv.osv): account_analytic_id = self._get_account_analytic_invoice(cursor, user, picking, sale_line) - account_id = self.pool.get('account.fiscal.position').map_account(cursor, user, picking.sale_id.partner_id.property_account_position, account_id) + account_id = fiscal_position_obj.map_account(cursor, user, picking.sale_id.partner_id.property_account_position, account_id) invoice = invoices[result[picking.id]] invoice_line_id = invoice_line_obj.create(cursor, user, { 'name': name, @@ -183,7 +189,7 @@ class stock_picking(osv.osv): 'account_analytic_id': account_analytic_id, 'notes':sale_line.notes }, context=context) - self.pool.get('sale.order.line').write(cursor, user, [sale_line.id], {'invoiced': True, + order_line_obj.write(cursor, user, [sale_line.id], {'invoiced': True, 'invoice_lines': [(6, 0, [invoice_line_id])], }) return result diff --git a/addons/sale_crm/wizard/crm_make_sale.py b/addons/sale_crm/wizard/crm_make_sale.py index 030a7567e57576651b05cf9b5c99f722325bce44..ae41ced671caf891dbad3d190a0903039204a0bb 100644 --- a/addons/sale_crm/wizard/crm_make_sale.py +++ b/addons/sale_crm/wizard/crm_make_sale.py @@ -108,7 +108,7 @@ class crm_make_sale(osv.osv_memory): new_ids.append(new_id) message = _("Opportunity '%s' is converted to Quotation.") % (case.name) self.log(cr, uid, case.id, message) - case_obj._history(cr, uid, [case], _("Converted to Sales Quotation(id: %s).") % (new_id)) + case_obj.message_append(cr, uid, [case], _("Converted to Sales Quotation(id: %s).") % (new_id), context=context) if make.close: case_obj.case_close(cr, uid, data) diff --git a/addons/share/__openerp__.py b/addons/share/__openerp__.py index f83f69abd1e9ab22e4b1af44db84ba1003cc5d20..52ffa165c146db66ea7cfdbe1d48eaf531ab94da 100644 --- a/addons/share/__openerp__.py +++ b/addons/share/__openerp__.py @@ -23,7 +23,7 @@ { "name" : "Sharing Tools", "version" : "1.5", - "depends" : ["base"], + "depends" : ["base", "mail"], "author" : "OpenERP SA", "category": 'Tools', 'complexity': "easy", diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py index eec8b440dfa125e58d4ac3eb11519ce2285c11ee..b66ccc2dd4779b84c2a99911e5d8e745cc96c4d8 100644 --- a/addons/share/wizard/share_wizard.py +++ b/addons/share/wizard/share_wizard.py @@ -211,7 +211,7 @@ class share_wizard(osv.osv_memory): del new_context[key] dataobj = self.pool.get('ir.model.data') - menu_id = dataobj._get_id(cr, uid, 'base', 'menu_administration_shortcut', new_context) + menu_id = dataobj._get_id(cr, uid, 'base', 'menu_administration_shortcut') shortcut_menu_id = int(dataobj.read(cr, uid, menu_id, ['res_id'], new_context)['res_id']) action_id = self.pool.get('ir.actions.act_window').create(cr, UID_ROOT, values, new_context) menu_data = {'name': values['name'], @@ -692,6 +692,7 @@ class share_wizard(osv.osv_memory): def send_emails(self, cr, uid, wizard_data, context=None): self._logger.info('Sending share notifications by email...') + mail_message = self.pool.get('mail.message') user = self.pool.get('res.users').browse(cr, UID_ROOT, uid) if not user.user_email: raise osv.except_osv(_('Email required'), _('The current user must have an email address configured in User Preferences to be able to send outgoing emails.')) @@ -726,7 +727,12 @@ class share_wizard(osv.osv_memory): body += _("OpenERP is a powerful and user-friendly suite of Business Applications (CRM, Sales, HR, etc.)\n" "It is open source and can be found on http://www.openerp.com.") - if tools.email_send(user.user_email, [email_to], subject, body): + if mail_message.schedule_with_attach(cr, uid, + user.user_email, + [email_to], + subject, + body, + model='share.wizard'): emails_sent += 1 else: self._logger.warning('Failed to send share notification from %s to %s, ignored', user.user_email, email_to) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 310b1067ff87770b303fdde15777eda5a070822a..8d7e993d817547adada9ee9c34aa06dd802adac7 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -309,7 +309,7 @@ class stock_location(osv.osv): # Compute based on pricetype # Choose the right filed standard_price to read - amount_unit = product.price_get('standard_price', context)[product.id] + amount_unit = product.price_get('standard_price', context=context)[product.id] price = qty[product_id] * amount_unit total_price += price @@ -891,7 +891,7 @@ class stock_picking(osv.osv): if type in ('in_invoice', 'in_refund'): # Take the user company and pricetype context['currency_id'] = move_line.company_id.currency_id.id - amount_unit = move_line.product_id.price_get('standard_price', context)[move_line.product_id.id] + amount_unit = move_line.product_id.price_get('standard_price', context=context)[move_line.product_id.id] return amount_unit else: return move_line.product_id.list_price @@ -1205,7 +1205,7 @@ class stock_picking(osv.osv): new_std_price = new_price else: # Get the standard price - amount_unit = product.price_get('standard_price', context)[product.id] + amount_unit = product.price_get('standard_price', context=context)[product.id] new_std_price = ((amount_unit * product_avail[product.id])\ + (new_price * qty))/(product_avail[product.id] + qty) # Write the field according to price type field @@ -2057,7 +2057,7 @@ class stock_move(osv.osv): if context is None: context = {} currency_ctx = dict(context, currency_id = move.company_id.currency_id.id) - amount_unit = move.product_id.price_get('standard_price', currency_ctx)[move.product_id.id] + amount_unit = move.product_id.price_get('standard_price', context=currency_ctx)[move.product_id.id] reference_amount = amount_unit * qty or 1.0 return reference_amount, reference_currency_id @@ -2443,7 +2443,7 @@ class stock_move(osv.osv): new_std_price = new_price else: # Get the standard price - amount_unit = product.price_get('standard_price', context)[product.id] + amount_unit = product.price_get('standard_price', context=context)[product.id] new_std_price = ((amount_unit * product.qty_available)\ + (new_price * qty))/(product.qty_available + qty) diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 7b2dae600d08c8558873737faf5d940c75f97b51..c04ac9ef62c9b1680cba420252a89de719dbe157 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -698,6 +698,7 @@ <field name="name" readonly="1"/> <field name="origin"/> <field name="address_id" on_change="onchange_partner_in(address_id)" context="{'contact_display':'partner'}" colspan="4"/> + <field name="invoice_state" string="Invoice Control" groups="base.group_extended"/> <field name="backorder_id" readonly="1" groups="base.group_extended"/> </group> <group colspan="2" col="2"> @@ -708,7 +709,7 @@ </group> <notebook colspan="4"> <page string="Products"> - <field colspan="4" name="move_lines" nolabel="1" widget="one2many_list" default_get="{'move_line':move_lines}"> + <field colspan="4" name="move_lines" nolabel="1" widget="one2many_list" default_get="{'move_line':move_lines, 'address_in_id': address_id}"> <tree colors="grey:scrapped == True" string="Stock Moves"> <field name="product_id"/> <field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos)"/> @@ -737,7 +738,6 @@ groups="base.group_extended" icon="terp-stock_effects-object-colorize" states="draft,assigned,confirmed"/> - <field name="location_id"/> <field name="location_dest_id"/> <field name="date_expected" string="Date Expected"/> <field name="state"/> @@ -760,7 +760,7 @@ <group colspan="2" col="2"> <separator string="Locations" colspan="2" /> - <field name="location_id" domain="[('usage','=','internal')]" /> + <field name="location_id" domain="[('usage','<>','view')]" /> <field name="location_dest_id" domain="[('usage','=','internal')]" /> </group> @@ -810,7 +810,6 @@ </page> <page string="Additional info" groups="base.group_extended,base.group_multi_company"> <field name="auto_picking" groups="base.group_extended"/> - <field name="invoice_state" groups="base.group_extended"/> <field name="date_done" groups="base.group_extended"/> <field name="move_type" groups="base.group_extended"/> <field name="type" groups="base.group_extended"/> @@ -1112,7 +1111,7 @@ <group colspan="4" col="4"> <field name="name" readonly="1"/> <field name="origin"/> - <field name="address_id" on_change="onchange_partner_in(address_id)" context="{'contact_display':'partner'}" colspan="4"/> + <field name="address_id" on_change="onchange_partner_in(address_id)" context="{'contact_display':'partner'}" domain="[('partner_id','<>',False)]" colspan="4"/> <field name="invoice_state" string="Invoice Control"/> <field name="backorder_id" readonly="1" groups="base.group_extended"/> </group> @@ -1573,7 +1572,7 @@ <field name="date"/> <field name="state"/> <button name="action_assign" states="confirmed" string="Set Available" type="object" icon="gtk-yes"/> - <button name="%(action_partial_move_server)d" string="Process" type="action" states="confirmed,assigned" icon="gtk-go-forward"/> + <button name="action_done" string="Process" type="object" states="confirmed,assigned" icon="gtk-go-forward"/> </tree> </field> </record> diff --git a/addons/stock/wizard/stock_invoice_onshipping.py b/addons/stock/wizard/stock_invoice_onshipping.py index 42add8019c4c11d71e1bfdc56e13a24036c719a9..395758b121bb588702c8cec9f3793439e479b738 100644 --- a/addons/stock/wizard/stock_invoice_onshipping.py +++ b/addons/stock/wizard/stock_invoice_onshipping.py @@ -25,6 +25,12 @@ from tools.translate import _ class stock_invoice_onshipping(osv.osv_memory): + def _get_journal(self, cr, uid, context=None): + res = self._get_journal_id(cr, uid, context=context) + if res: + return res[0] + return False + def _get_journal_id(self, cr, uid, context=None): if context is None: context = {} @@ -61,7 +67,6 @@ class stock_invoice_onshipping(osv.osv_memory): vals.append(t1) return vals - _name = "stock.invoice.onshipping" _description = "Stock Invoice Onshipping" @@ -70,7 +75,11 @@ class stock_invoice_onshipping(osv.osv_memory): 'group': fields.boolean("Group by partner"), 'invoice_date': fields.date('Invoiced date'), } - + + _defaults = { + 'journal_id' : _get_journal, + } + def view_init(self, cr, uid, fields_list, context=None): if context is None: context = {} diff --git a/addons/stock/wizard/stock_partial_move.py b/addons/stock/wizard/stock_partial_move.py index 874b22844d1ada3e13add861b6d9856e6c7c78d4..234133c02ce08969ccac42cc074e8ea9434a1b38 100644 --- a/addons/stock/wizard/stock_partial_move.py +++ b/addons/stock/wizard/stock_partial_move.py @@ -32,6 +32,8 @@ class stock_partial_move_memory_out(osv.osv_memory): 'quantity' : fields.float("Quantity", required=True), 'product_uom': fields.many2one('product.uom', 'Unit of Measure', required=True), 'prodlot_id' : fields.many2one('stock.production.lot', 'Production Lot'), + 'location_id': fields.many2one('stock.location', 'Location', required=True), + 'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True), 'move_id' : fields.many2one('stock.move', "Move"), 'wizard_id' : fields.many2one('stock.partial.move', string="Wizard"), 'cost' : fields.float("Cost", help="Unit Cost for this product line"), diff --git a/addons/stock/wizard/stock_partial_move_view.xml b/addons/stock/wizard/stock_partial_move_view.xml index ec33dff083b30d27a664173b72920297cb11d6fb..8d4be66b2430bf64c1666e6924e4016db095fc4c 100644 --- a/addons/stock/wizard/stock_partial_move_view.xml +++ b/addons/stock/wizard/stock_partial_move_view.xml @@ -30,6 +30,8 @@ <field name="product_id" /> <field name="quantity" /> <field name="product_uom" /> + <field name="location_id" /> + <field name="location_dest_id" /> <field name="prodlot_id" domain="[('product_id', '=', product_id)]" groups="base.group_extended" /> <field name="cost" /> <field name="currency" /> @@ -46,6 +48,8 @@ <field name="product_id" /> <field name="quantity" /> <field name="product_uom" /> + <field name="location_id" /> + <field name="location_dest_id" /> <field name="prodlot_id" domain="[('product_id', '=', product_id)]" groups="base.group_extended" /> <field name="cost" /> <field name="currency" /> @@ -62,6 +66,8 @@ <field name="product_id" /> <field name="quantity" /> <field name="product_uom" /> + <field name="location_id" /> + <field name="location_dest_id" /> <field name="prodlot_id" domain="[('product_id', '=', product_id)]" groups="base.group_extended" /> </tree> </field> @@ -76,6 +82,8 @@ <field name="product_id" /> <field name="quantity" /> <field name="product_uom" /> + <field name="location_id" /> + <field name="location_dest_id" /> <field name="prodlot_id" domain="[('product_id', '=', product_id)]" groups="base.group_extended" /> </form> </field> diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py index 421bcae10ba028c61b2f990336f6db42bfebc8cb..6549b71773d13d48e19808e7a17c2d25c61c0741 100644 --- a/addons/stock/wizard/stock_partial_picking.py +++ b/addons/stock/wizard/stock_partial_picking.py @@ -122,7 +122,9 @@ class stock_partial_picking(osv.osv_memory): 'quantity' : picking.product_qty, 'product_uom' : picking.product_uom.id, 'prodlot_id' : picking.prodlot_id.id, - 'move_id' : picking.id, + 'move_id' : picking.id, + 'location_id' : picking.location_id.id, + 'location_dest_id' : picking.location_dest_id.id, } if pick_type == 'in': @@ -142,19 +144,32 @@ class stock_partial_picking(osv.osv_memory): @return: A dictionary which of fields with values. """ pick_obj = self.pool.get('stock.picking') - + stock_move_obj = self.pool.get('stock.move') picking_ids = context.get('active_ids', False) partial = self.browse(cr, uid, ids[0], context=context) partial_datas = { 'delivery_date' : partial.date } - for pick in pick_obj.browse(cr, uid, picking_ids, context=context): picking_type = self.get_picking_type(cr, uid, pick, context=context) moves_list = picking_type == 'in' and partial.product_moves_in or partial.product_moves_out - for move in moves_list: - partial_datas['move%s' % (move.move_id.id)] = { + if not move.move_id.id: + seq_obj_name = 'stock.picking.' + picking_type + move_id = stock_move_obj.create(cr,uid,{'name' : self.pool.get('ir.sequence').get(cr, uid, seq_obj_name), + 'product_id': move.product_id.id, + 'product_qty': move.quantity, + 'product_uom': move.product_uom.id, + 'prodlot_id': move.prodlot_id.id, + 'location_id' : move.location_id.id, + 'location_dest_id' : move.location_dest_id.id, + 'picking_id': pick.id + },context=context) + stock_move_obj.action_done(cr, uid, [move_id], context) + else: + move_id = move.move_id.id + + partial_datas['move%s' % (move_id)] = { 'product_id': move.id, 'product_qty': move.quantity, 'product_uom': move.product_uom.id, diff --git a/addons/survey/__openerp__.py b/addons/survey/__openerp__.py index e44efbe0bf9c19bce490ef66f9efbfe2e16ba8d3..5a551c28a3d95e18dfe916701039553a9fac519c 100644 --- a/addons/survey/__openerp__.py +++ b/addons/survey/__openerp__.py @@ -33,7 +33,7 @@ Different users may give different answers of question and according to that sur Partners are also sent mails with user name and password for the invitation of the survey """, 'author': 'OpenERP SA', - 'depends': ['base_tools'], + 'depends': ['base_tools', 'mail'], 'update_xml': ['survey_report.xml', 'survey_data.xml', 'wizard/survey_selection.xml', diff --git a/addons/survey/wizard/survey_answer.py b/addons/survey/wizard/survey_answer.py index 896bde70d3c2b1eac89d0d54db0ab0a0996bf389..471290af2cb6f69391220b157823c5696fc3c5e1 100644 --- a/addons/survey/wizard/survey_answer.py +++ b/addons/survey/wizard/survey_answer.py @@ -49,7 +49,7 @@ class survey_question_wiz(osv.osv_memory): @param context: A standard dictionary for contextual values @return : Dictionary value for created view of particular survey pages. """ - + result = super(survey_question_wiz, self).fields_view_get(cr, uid, view_id, \ view_type, context, toolbar,submenu) @@ -61,6 +61,7 @@ class survey_question_wiz(osv.osv_memory): sur_response_obj = self.pool.get('survey.response') que_col_head = self.pool.get('survey.question.column.heading') user_obj = self.pool.get('res.users') + mail_message = self.pool.get('mail.message') if context is None: context = {} if view_type in ['form']: @@ -79,7 +80,7 @@ class survey_question_wiz(osv.osv_memory): wiz_id = surv_name_wiz.create(cr, uid, res_data) sur_name_rec = surv_name_wiz.browse(cr, uid, wiz_id, context=context) context.update({'sur_name_id' :wiz_id}) - + if context.has_key('active_id'): context.pop('active_id') @@ -401,7 +402,7 @@ class survey_question_wiz(osv.osv_memory): response_id = surv_name_wiz.read(cr, uid, context.get('sur_name_id',False))['response'] context.update({'response_id':response_id}) report = self.create_report(cr, uid, [int(survey_id)], 'report.survey.browse.response', survey_data.title,context) - attachments = [] + attachments = {} file = open(addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf") file_data = "" while 1: @@ -410,7 +411,7 @@ class survey_question_wiz(osv.osv_memory): if not line: break - attachments.append((survey_data.title + ".pdf",file_data)) + attachments[survey_data.title + ".pdf"] = file_data file.close() os.remove(addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf") user_email = False @@ -428,7 +429,7 @@ class survey_question_wiz(osv.osv_memory): if user_email and resp_email: user_name = user_obj.browse(cr, uid, uid, context=context).name mail = "Hello " + survey_data.responsible_id.name + ",\n\n " + str(user_name) + " Give Response Of " + survey_data.title + " Survey.\n\n Thanks," - tools.email_send(user_email, [resp_email], "Survey Answer Of " + str(user_name) , mail, attach = attachments) + mail_message.schedule_with_attach(cr, uid, user_email, [resp_email], "Survey Answer Of " + str(user_name) , mail, attachments=attachments, context=context) xml_form = etree.Element('form', {'string': _('Complete Survey Answer')}) etree.SubElement(xml_form, 'separator', {'string': 'Complete Survey', 'colspan': "4"}) @@ -447,7 +448,7 @@ class survey_question_wiz(osv.osv_memory): @param self: The object pointer @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, + @param uid: the current user’s ID for security checks, @param res_ids: List of survey answer IDs, @param report_name: name of the report, @param file_name: To give file name of the report, diff --git a/addons/survey/wizard/survey_send_invitation.py b/addons/survey/wizard/survey_send_invitation.py index 49cbac82b462b9f997d756677c10cec020892d4a..e57c8f91d8b0df9e5375c4f1619bd2392065067a 100644 --- a/addons/survey/wizard/survey_send_invitation.py +++ b/addons/survey/wizard/survey_send_invitation.py @@ -102,6 +102,7 @@ class survey_send_invitation(osv.osv_memory): partner_ids = record['partner_ids'] user_ref= self.pool.get('res.users') survey_ref= self.pool.get('survey') + mail_message = self.pool.get('mail.message') model_data_obj = self.pool.get('ir.model.data') group_id = model_data_obj._get_id(cr, uid, 'base', 'group_survey_user') @@ -118,7 +119,7 @@ class survey_send_invitation(osv.osv_memory): res_user = "" user_exists = False new_user = [] - attachments = [] + attachments = {} current_sur = survey_ref.browse(cr, uid, context.get('active_id'), context=context) exist_user = current_sur.invited_user_ids if exist_user: @@ -133,8 +134,8 @@ class survey_send_invitation(osv.osv_memory): file_data += line if not line: break - attachments.append((id.title +".pdf",file_data)) file.close() + attachments[id.title +".pdf"] = file_data os.remove(addons.get_module_resource('survey', 'report') + id.title +".pdf") for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids): @@ -151,8 +152,8 @@ class survey_send_invitation(osv.osv_memory): mail = record['mail']%{'login':addr.email, 'passwd':user.password, \ 'name' : addr.name} if record['send_mail_existing']: - tools.email_send(record['mail_from'], [addr.email] , \ - record['mail_subject_existing'] , mail) + mail_message.schedule_with_attach(cr, uid, record['mail_from'], [addr.email] , \ + record['mail_subject_existing'] , mail, context=context) existing+= "- %s (Login: %s, Password: %s)\n" % (user.name, addr.email, \ user.password) continue @@ -162,8 +163,8 @@ class survey_send_invitation(osv.osv_memory): mail = record['mail']%{'login': user_email.login, \ 'passwd': user_email.password, 'name': addr.name} if record['send_mail_existing']: - tools.email_send(record['mail_from'], [addr.email],\ - record['mail_subject_existing'], mail) + mail_message.schedule_with_attach(cr, uid, record['mail_from'], [addr.email],\ + record['mail_subject_existing'], mail, context=context) res_user+= "- %s (Login: %s, Password: %s)\n" % \ (user_email.name, user_email.login, user_email.password) continue @@ -175,8 +176,8 @@ class survey_send_invitation(osv.osv_memory): out+= addr.email + ',' + passwd + '\n' mail= record['mail'] % {'login' : addr.email, 'passwd' : passwd, 'name' : addr.name} if record['send_mail']: - ans = tools.email_send(record['mail_from'], [addr.email], \ - record['mail_subject'], mail,attach = attachments) + ans = mail_message.schedule_with_attach(cr, uid, record['mail_from'], [addr.email], \ + record['mail_subject'], mail, attachments=attachments, context=context) if ans: res_data = {'name': addr.name or 'Unknown', diff --git a/addons/thunderbird/__openerp__.py b/addons/thunderbird/__openerp__.py index 1729cc430cf744f90b9e429dada50dfdd5d2c1e6..51e4153e897b2b288eabf2146ccb1941fdf5cf08 100644 --- a/addons/thunderbird/__openerp__.py +++ b/addons/thunderbird/__openerp__.py @@ -24,7 +24,7 @@ "version" : "1.0", "author" : ['OpenERP SA', 'Axelor'], "website" : "http://www.openerp.com/", - "depends" : ["base","mail_gateway"], + "depends" : ["base","mail"], "category" : "Tools", "description": """ This module is required for the Thuderbird Plug-in to work properly. @@ -35,7 +35,6 @@ OpenERP objects. You can select a partner, a task, a project, an analytical account, or any other object and attach the selected mail as a .eml file in the attachment of a selected record. You can create documents for CRM Lead, HR Applicant and Project Issue from selected mails. - """, "init_xml" : [], "demo_xml" : [], diff --git a/addons/thunderbird/partner/partner.py b/addons/thunderbird/partner/partner.py index c506cf58b4f347eda313b8214af20976bf0cddb5..80a4bfa4d98bed0228ad415ef013c510922aec4c 100644 --- a/addons/thunderbird/partner/partner.py +++ b/addons/thunderbird/partner/partner.py @@ -23,113 +23,7 @@ from osv import osv import base64 import email import tools -import binascii -import dateutil.parser -class email_server_tools(osv.osv_memory): - _inherit = "email.server.tools" - def history_message(self, cr, uid, model, res_id, message, context=None): - #@param message: string of mail which is read from EML File - attachment_pool = self.pool.get('ir.attachment') - msg = self.parse_message(message) - attachments = msg.get('attachments', []) - att_ids = [] - for attachment in attachments: - data_attach = { - 'name': attachment, - 'datas': binascii.b2a_base64(str(attachments.get(attachment))), - 'datas_fname': attachment, - 'description': 'Mail attachment From Thunderbird msg_id: %s' %(msg.get('message_id', '')), - 'res_model': model, - 'res_id': res_id, - } - att_ids.append(attachment_pool.create(cr, uid, data_attach)) - return self.history(cr, uid, model, res_id, msg, att_ids) - - def parse_message(self, message): - #TOCHECK: put this function in mailgateway module - if isinstance(message, unicode): - message = message.encode('utf-8') - msg_txt = email.message_from_string(message) - message_id = msg_txt.get('message-id', False) - msg = {} - fields = msg_txt.keys() - msg['id'] = message_id - msg['message-id'] = message_id - if 'Subject' in fields: - msg['subject'] = self._decode_header(msg_txt.get('Subject')) - - if 'Content-Type' in fields: - msg['content-type'] = msg_txt.get('Content-Type') - - if 'From' in fields: - msg['from'] = self._decode_header(msg_txt.get('From')) - - if 'Delivered-To' in fields: - msg['to'] = self._decode_header(msg_txt.get('Delivered-To')) - - if 'CC' in fields: - msg['cc'] = self._decode_header(msg_txt.get('CC')) - - if 'Reply-to' in fields: - msg['reply'] = self._decode_header(msg_txt.get('Reply-To')) - - if 'Date' in fields: - date = self._decode_header(msg_txt.get('Date')) - msg['date'] = dateutil.parser.parse(date).strftime("%Y-%m-%d %H:%M:%S") - - if 'Content-Transfer-Encoding' in fields: - msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') - - if 'References' in fields: - msg['references'] = msg_txt.get('References') - - if 'In-Reply-To' in fields: - msg['in-reply-to'] = msg_txt.get('In-Reply-To') - - if 'X-Priority' in fields: - msg['priority'] = msg_txt.get('X-Priority', '3 (Normal)').split(' ')[0] - - if not msg_txt.is_multipart() or 'text/plain' in msg.get('Content-Type', ''): - encoding = msg_txt.get_content_charset() - body = msg_txt.get_payload(decode=True) - msg['body'] = tools.ustr(body, encoding) - - attachments = {} - has_plain_text = False - if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', ''): - body = "" - for part in msg_txt.walk(): - if part.get_content_maintype() == 'multipart': - continue - - encoding = part.get_content_charset() - filename = part.get_filename() - if part.get_content_maintype()=='text': - content = part.get_payload(decode=True) - if filename: - attachments[filename] = content - elif not has_plain_text: - # main content parts should have 'text' maintype - # and no filename. we ignore the html part if - # there is already a plaintext part without filename, - # because presumably these are alternatives. - content = tools.ustr(content, encoding) - if part.get_content_subtype() == 'html': - body = tools.ustr(tools.html2plaintext(content)) - elif part.get_content_subtype() == 'plain': - body = content - has_plain_text = True - elif part.get_content_maintype() in ('application', 'image'): - if filename : - attachments[filename] = part.get_payload(decode=True) - else: - res = part.get_payload(decode=True) - body += tools.ustr(res, encoding) - - msg['body'] = body - msg['attachments'] = attachments - return msg -email_server_tools() +from tools.translate import _ class thunderbird_partner(osv.osv_memory): _name = "thunderbird.partner" @@ -151,21 +45,21 @@ class thunderbird_partner(osv.osv_memory): ref_ids = str(dictcreate.get('ref_ids')).split(';') msg = dictcreate.get('message') mail = msg - msg = self.pool.get('email.server.tools').parse_message(msg) - server_tools_pool = self.pool.get('email.server.tools') + mail_message = self.pool.get('mail.message') + msg = mail_message.parse_message(msg) + subject = msg.get('Subject', False) + thread_pool = self.pool.get('mail.thread') message_id = msg.get('message-id', False) - msg_pool = self.pool.get('mailgate.message') msg_ids = [] res = {} res_ids = [] obj_list= ['crm.lead','project.issue','hr.applicant','res.partner'] for ref_id in ref_ids: - msg_new = dictcreate.get('message') ref = ref_id.split(',') model = ref[0] res_id = int(ref[1]) if message_id: - msg_ids = msg_pool.search(cr, uid, [('message_id','=',message_id),('res_id','=',res_id),('model','=',model)]) + msg_ids = mail_message.search(cr, uid, [('message_id','=',message_id),('res_id','=',res_id),('model','=',model)]) if msg_ids and len(msg_ids): continue if model not in obj_list: @@ -193,7 +87,23 @@ class thunderbird_partner(osv.osv_memory): res['datas'] = base64.b64encode(mail) res['res_id'] = res_id obj_attch.create(cr, uid, res) - server_tools_pool.history_message(cr, uid, model, res_id, msg_new) + threads = self.pool.get(model).browse(cr, uid, res_id) + + thread_pool.message_append(cr, uid, + [threads], + subject = msg.get('subject'), + details = msg.get('body_text'), + email_to = msg.get('to'), + email_from = msg.get('from'), + email_cc = msg.get('cc'), + message_id = msg.get('message-id'), + references = msg.get('references', False) or msg.get('in-reply-to', False), + attachments = msg.get('attachments', {}), + email_date = msg.get('date'), + body_html= msg.get('body_html'), + subtype = msg.get('subtype'), + headers = msg.get('headers'), + reply_to = msg.get('reply')) res_ids.append(res_id) return len(res_ids) @@ -201,7 +111,7 @@ class thunderbird_partner(osv.osv_memory): dictcreate = dict(vals) model = str(dictcreate.get('model')) message = dictcreate.get('message') - return self.pool.get('email.server.tools').process_email(cr, uid, model, message, attach=True, context=None) + return self.pool.get('mail.thread').message_process(cr, uid, model, message) def search_message(self, cr, uid, message, context=None): #@param message: string of mail which is read from EML File @@ -209,26 +119,26 @@ class thunderbird_partner(osv.osv_memory): references = [] dictcreate = dict(message) msg = dictcreate.get('message') - msg = self.pool.get('email.server.tools').parse_message(msg) + msg = self.pool.get('mail.message').parse_message(msg) message_id = msg.get('message-id') refs = msg.get('references',False) references = False if refs: references = refs.split() - msg_pool = self.pool.get('mailgate.message') + mail_message = self.pool.get('mail.message') model = '' res_id = 0 if message_id: - msg_ids = msg_pool.search(cr, uid, [('message_id','=',message_id)]) + msg_ids = mail_message.search(cr, uid, [('message_id','=',message_id)]) if msg_ids and len(msg_ids): - msg = msg_pool.browse(cr, uid, msg_ids[0]) + msg = mail_message.browse(cr, uid, msg_ids[0]) model = msg.model res_id = msg.res_id else: if references : - msg_ids = msg_pool.search(cr, uid, [('message_id','in',references)]) + msg_ids = mail_message.search(cr, uid, [('message_id','in',references)]) if msg_ids and len(msg_ids): - msg = msg_pool.browse(cr, uid, msg_ids[0]) + msg = mail_message.browse(cr, uid, msg_ids[0]) model = msg.model res_id = msg.res_id return (model,res_id)