diff options
| author | Mustufa Rangwala (OpenERP) <mra@tinyerp.com> | 2012-07-23 14:31:08 +0530 |
|---|---|---|
| committer | Mustufa Rangwala (OpenERP) <mra@tinyerp.com> | 2012-07-23 14:31:08 +0530 |
| commit | ef38540e3c38dcc4733d764754aed2f300eff424 (patch) | |
| tree | 78ed6ffcc81add7d0eeb6bc6b8ec0a0e88de4520 /addons | |
| parent | bc9533ab82ec482a308a2964f718917c570d6894 (diff) | |
| parent | 61f361bfee1ae021aedef0ebbf3c91d15083cf1a (diff) | |
[MERGE] Merge from main addons
bzr revid: mra@tinyerp.com-20120723090108-tdzfr7t02mrx9or4
Diffstat (limited to 'addons')
199 files changed, 6748 insertions, 1854 deletions
diff --git a/addons/account/account.py b/addons/account/account.py index fab9de5407c..335822b7299 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1788,7 +1788,7 @@ class account_tax_code(osv.osv): 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'), 'company_id': fields.many2one('res.company', 'Company', required=True), 'sign': fields.float('Coefficent for parent', required=True, help='You can specify here the coefficient that will be used when consolidating the amount of this case into its parent. For example, set 1/-1 if you want to add/substract it.'), - 'notprintable':fields.boolean("Not Printable in Invoice", help="Check this box if you don't want any VAT related to this Tax Code to appear on invoices"), + 'notprintable':fields.boolean("Not Printable in Invoice", help="Check this box if you don't want any tax related to this tax code to appear on invoices"), 'sequence': fields.integer('Sequence', help="Determine the display order in the report 'Accounting \ Reporting \ Generic Reporting \ Taxes \ Taxes Report'"), } @@ -1880,17 +1880,17 @@ class account_tax(osv.osv): 'python_applicable':fields.text('Python Code'), # - # Fields used for the VAT declaration + # Fields used for the Tax declaration # - 'base_code_id': fields.many2one('account.tax.code', 'Account Base Code', help="Use this code for the VAT declaration."), - 'tax_code_id': fields.many2one('account.tax.code', 'Account Tax Code', help="Use this code for the VAT declaration."), + 'base_code_id': fields.many2one('account.tax.code', 'Account Base Code', help="Use this code for the tax declaration."), + 'tax_code_id': fields.many2one('account.tax.code', 'Account Tax Code', help="Use this code for the tax declaration."), 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), # Same fields for refund invoices - 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."), - 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."), + 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the tax declaration."), + 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the tax declaration."), 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), 'include_base_amount': fields.boolean('Included in base amount', help="Indicates if the amount of tax must be included in the base amount for the computation of the next taxes"), @@ -2217,7 +2217,7 @@ class account_tax(osv.osv): def compute_inv(self, cr, uid, taxes, price_unit, quantity, product=None, partner=None, precision=None): """ Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID. - Price Unit is a VAT included price + Price Unit is a Tax included price RETURN: [ tax ] @@ -2673,7 +2673,7 @@ class account_tax_code_template(osv.osv): 'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True), 'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Child Codes'), 'sign': fields.float('Sign For Parent', required=True), - 'notprintable':fields.boolean("Not Printable in Invoice", help="Check this box if you don't want any VAT related to this Tax Code to appear on invoices"), + 'notprintable':fields.boolean("Not Printable in Invoice", help="Check this box if you don't want any tax related to this tax Code to appear on invoices."), } _defaults = { @@ -2788,17 +2788,17 @@ class account_tax_template(osv.osv): 'python_applicable':fields.text('Python Code'), # - # Fields used for the VAT declaration + # Fields used for the Tax declaration # - 'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."), - 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."), + 'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the tax declaration."), + 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the tax declaration."), 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), # Same fields for refund invoices - 'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."), - 'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."), + 'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the tax declaration."), + 'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the tax declaration."), 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), 'include_base_amount': fields.boolean('Include in Base Amount', help="Set if the amount of tax must be included in the base amount before computing the next taxes."), diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 57cc125bb5b..5eb56bfa645 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -327,7 +327,7 @@ class account_move_line(osv.osv): if account and ((not fields) or ('debit' in fields) or ('credit' in fields)): data['account_id'] = account.id - # Propose the price VAT excluded, the VAT will be added when confirming line + # Propose the price Tax excluded, the Tax will be added when confirming line if account.tax_ids: taxes = fiscal_pos_obj.map_tax(cr, uid, part and part.property_account_position or False, account.tax_ids) tax = tax_obj.browse(cr, uid, taxes) @@ -1347,7 +1347,7 @@ class account_move_line(osv.osv): } if data['tax_code_id']: self.create(cr, uid, data, context) - #create the VAT movement + #create the Tax movement data = { 'move_id': vals['move_id'], 'name': tools.ustr(vals['name'] or '') + ' ' + tools.ustr(tax['name'] or ''), diff --git a/addons/account/i18n/account.pot b/addons/account/i18n/account.pot index b092b3a2f8c..174002a8d8d 100644 --- a/addons/account/i18n/account.pot +++ b/addons/account/i18n/account.pot @@ -203,7 +203,7 @@ msgstr "" #. module: account #: help:account.tax.code,notprintable:0 #: help:account.tax.code.template,notprintable:0 -msgid "Check this box if you don't want any VAT related to this Tax Code to appear on invoices" +msgid "Check this box if you don't want any tax related to this tax code to appear on invoices" msgstr "" #. module: account @@ -1853,7 +1853,7 @@ msgstr "" #. module: account #: report:account.journal.period.print.sale.purchase:0 -msgid "VAT Declaration" +msgid "Tax Declaration" msgstr "" #. module: account @@ -2249,7 +2249,7 @@ msgstr "" #. module: account #: view:account.vat.declaration:0 -msgid "This menu prints a VAT declaration based on invoices or payments. Select one or several periods of the fiscal year. The information required for a tax declaration is automatically generated by OpenERP from invoices (or payments, in some countries). This data is updated in real time. That’s very useful because it enables you to preview at any time the tax that you owe at the start and end of the month or quarter." +msgid "This menu prints a tax declaration based on invoices or payments. Select one or several periods of the fiscal year. The information required for a tax declaration is automatically generated by OpenERP from invoices (or payments, in some countries). This data is updated in real time. That’s very useful because it enables you to preview at any time the tax that you owe at the start and end of the month or quarter." msgstr "" #. module: account @@ -4480,7 +4480,7 @@ msgstr "" #. module: account #: model:ir.actions.act_window,name:account.action_account_vat_declaration #: model:ir.model,name:account.model_account_vat_declaration -msgid "Account Vat Declaration" +msgid "Account Tax Declaration" msgstr "" #. module: account @@ -4821,7 +4821,7 @@ msgstr "" #: help:account.tax.template,ref_base_code_id:0 #: help:account.tax.template,ref_tax_code_id:0 #: help:account.tax.template,tax_code_id:0 -msgid "Use this code for the VAT declaration." +msgid "Use this code for the tax declaration." msgstr "" #. module: account @@ -5247,7 +5247,7 @@ msgstr "" #. module: account #: report:account.journal.period.print.sale.purchase:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: account @@ -7561,7 +7561,7 @@ msgstr "" #. module: account #: model:ir.actions.act_window,help:account.action_account_vat_declaration -msgid "This menu print a VAT declaration based on invoices or payments. You can select one or several periods of the fiscal year. Information required for a tax declaration is automatically generated by OpenERP from invoices (or payments, in some countries). This data is updated in real time. That’s very useful because it enables you to preview at any time the tax that you owe at the start and end of the month or quarter." +msgid "This menu print a tax declaration based on invoices or payments. You can select one or several periods of the fiscal year. Information required for a tax declaration is automatically generated by OpenERP from invoices (or payments, in some countries). This data is updated in real time. That’s very useful because it enables you to preview at any time the tax that you owe at the start and end of the month or quarter." msgstr "" #. module: account diff --git a/addons/account/partner_view.xml b/addons/account/partner_view.xml index cd75daa6c07..1e360f6c742 100644 --- a/addons/account/partner_view.xml +++ b/addons/account/partner_view.xml @@ -78,18 +78,19 @@ <page string="Accounting" col="4"> <group> <group> - <field name="property_account_receivable" groups="account.group_account_invoice" /> <field name="property_account_position" widget="selection"/> - <field name="property_payment_term" widget="selection"/> </group> <group> - <field name="property_account_payable" groups="account.group_account_invoice"/> + <field name="last_reconciliation_date"/> </group> <group> + <field name="property_account_receivable" groups="account.group_account_invoice" /> + <field name="property_payment_term" widget="selection"/> <field name="credit"/> <field name="credit_limit"/> </group> <group> + <field name="property_account_payable" groups="account.group_account_invoice"/> <field name="debit"/> </group> </group> @@ -142,16 +143,5 @@ view_type="form" view_mode="tree,form,graph,calendar"/> - <record id="view_res_partner_reconcile" model="ir.ui.view"> - <field name="name">res.partner.form.reconcile</field> - <field name="model">res.partner</field> - <field name="inherit_id" ref="base.view_partner_form"/> - <field name="arch" type="xml"> - <field name="credit_limit" position="after"> - <field name="last_reconciliation_date"/> - </field> - </field> - </record> - </data> </openerp> diff --git a/addons/account/report/account_journal_sale_purchase.rml b/addons/account/report/account_journal_sale_purchase.rml index 68f93b88294..236879669f9 100644 --- a/addons/account/report/account_journal_sale_purchase.rml +++ b/addons/account/report/account_journal_sale_purchase.rml @@ -219,7 +219,7 @@ <td><para style="P10a">Account</para></td> <td><para style="P10a">Partner</para></td> <td><para style="P10a">Label</para></td> - <td><para style="P10a">VAT</para></td> + <td><para style="P10a">Tax</para></td> <td><para style="P11"></para></td> <td><para style="P10b">Debit</para></td> <td><para style="P10b">Credit</para></td> @@ -271,7 +271,7 @@ <td><para style="P10a">Account</para></td> <td><para style="P10a">Partner</para></td> <td><para style="P10a">Label</para></td> - <td><para style="P10a">VAT</para></td> + <td><para style="P10a">Tax</para></td> <td><para style="P11"></para></td> <td><para style="P10b">Debit</para></td> <td><para style="P10b">Credit</para></td> @@ -330,7 +330,7 @@ <blockTable colWidths="15.0,80.0,20.0,182.0" style="Table1" repeatRows="1"> <tr> <td><para style="P12"></para></td> - <td><para style="P12">VAT Declaration</para></td> + <td><para style="P12">Tax Declaration</para></td> <td><para style="P12"></para></td> <td><para style="P12"></para></td> </tr> diff --git a/addons/account/wizard/account_vat_view.xml b/addons/account/wizard/account_vat_view.xml index 988c4786bb8..acafb55e208 100644 --- a/addons/account/wizard/account_vat_view.xml +++ b/addons/account/wizard/account_vat_view.xml @@ -3,12 +3,12 @@ <data> <record id="view_account_vat_declaration" model="ir.ui.view"> - <field name="name">Account Vat Declaration</field> + <field name="name">Account Tax Declaration</field> <field name="model">account.vat.declaration</field> <field name="type">form</field> <field name="arch" type="xml"> <form string="Taxes Report" version="7.0"> - <label colspan="4" string="This menu prints a VAT declaration based on invoices or payments. Select one or several periods of the fiscal year. The information required for a tax declaration is automatically generated by OpenERP from invoices (or payments, in some countries). This data is updated in real time. That’s very useful because it enables you to preview at any time the tax that you owe at the start and end of the month or quarter."/> + <label colspan="4" string="This menu prints a tax declaration based on invoices or payments. Select one or several periods of the fiscal year. The information required for a tax declaration is automatically generated by OpenERP from invoices (or payments, in some countries). This data is updated in real time. That’s very useful because it enables you to preview at any time the tax that you owe at the start and end of the month or quarter."/> <group string="Taxes Report" col="4"> <field name="chart_tax_id" widget='selection'/> <field name="fiscalyear_id"/> @@ -30,13 +30,13 @@ </record> <record id="action_account_vat_declaration" model="ir.actions.act_window"> - <field name="name">Account Vat Declaration</field> + <field name="name">Account Tax Declaration</field> <field name="type">ir.actions.act_window</field> <field name="res_model">account.vat.declaration</field> <field name="view_type">form</field> <field name="view_mode">form</field> <field name="target">new</field> - <field name="help">This menu print a VAT declaration based on invoices or payments. You can select one or several periods of the fiscal year. Information required for a tax declaration is automatically generated by OpenERP from invoices (or payments, in some countries). This data is updated in real time. That’s very useful because it enables you to preview at any time the tax that you owe at the start and end of the month or quarter.</field> + <field name="help">This menu print a tax declaration based on invoices or payments. You can select one or several periods of the fiscal year. Information required for a tax declaration is automatically generated by OpenERP from invoices (or payments, in some countries). This data is updated in real time. That’s very useful because it enables you to preview at any time the tax that you owe at the start and end of the month or quarter.</field> </record> <menuitem diff --git a/addons/account_accountant/__openerp__.py b/addons/account_accountant/__openerp__.py index f1155311f39..4bdca34e5f3 100644 --- a/addons/account_accountant/__openerp__.py +++ b/addons/account_accountant/__openerp__.py @@ -24,6 +24,7 @@ "author" : "OpenERP SA", "category": 'Accounting & Finance', "sequence": 10, + "summary": "Accounts Moves, Journals, Chart of Accounts", "description": """ Accounting Access Rights. ========================= diff --git a/addons/account_voucher/__openerp__.py b/addons/account_voucher/__openerp__.py index a7899338b8e..c943724f5f2 100644 --- a/addons/account_voucher/__openerp__.py +++ b/addons/account_voucher/__openerp__.py @@ -23,6 +23,7 @@ "name" : "eInvoicing & Payments", "version" : "1.0", "author" : 'OpenERP SA', + "summary": 'Supplier & Customer Invoices, Payments', "description": """ Account Voucher module includes all the basic requirements of Voucher Entries for Bank, Cash, Sales, Purchase, Expanse, Contra, etc. ==================================================================================================================================== diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index a9e5949a3ca..e89a52aa0e5 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -515,7 +515,7 @@ property or property parameter."), sub, body, attachments=attach and {'invitation.ics': attach} or None, - subtype='html', + content_subtype='html', reply_to=email_from, context=context ) diff --git a/addons/base_calendar/base_calendar_data.xml b/addons/base_calendar/base_calendar_data.xml index 3c20bf5fad5..727a0703db2 100644 --- a/addons/base_calendar/base_calendar_data.xml +++ b/addons/base_calendar/base_calendar_data.xml @@ -2,7 +2,7 @@ <openerp> <data noupdate="1"> - <record model="res.request.link" id="request_link_meeting"> + <record model="res.request.link" id="request_link_event"> <field name="name">Event</field> <field name="object">calendar.event</field> </record> diff --git a/addons/base_calendar/wizard/base_calendar_invite_attendee.py b/addons/base_calendar/wizard/base_calendar_invite_attendee.py index 676f9b3fdd2..5da711cf657 100644 --- a/addons/base_calendar/wizard/base_calendar_invite_attendee.py +++ b/addons/base_calendar/wizard/base_calendar_invite_attendee.py @@ -78,6 +78,7 @@ send an Email to Invited Person') att_obj = self.pool.get('calendar.attendee') user_obj = self.pool.get('res.users') current_user = user_obj.browse(cr, uid, uid, context=context) + for datas in self.read(cr, uid, ids, context=context): type = datas.get('type') vals = [] diff --git a/addons/base_vat/base_vat_view.xml b/addons/base_vat/base_vat_view.xml index 1d7f06eb571..df1d18ac194 100644 --- a/addons/base_vat/base_vat_view.xml +++ b/addons/base_vat/base_vat_view.xml @@ -7,7 +7,7 @@ <field name="model">res.partner</field> <field name="inherit_id" ref="account.view_partner_property_form"/> <field name="arch" type="xml"> - <field name="property_account_payable" position="after" version="7.0"> + <field name="property_account_position" position="after" version="7.0"> <label for="vat"/> <div> <field name="vat" on_change="vat_change(vat)" placeholder="BE0477472702" class="oe_inline"/> diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index 9fb1a4db28d..bc0a9e6cf3d 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -25,6 +25,7 @@ 'version': '1.0', 'category': 'Customer Relationship Management', "sequence": 2, + "summary": "Agenda, Leads, Opportunities", 'description': """ The generic OpenERP Customer Relationship Management. ===================================================== @@ -84,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_add_note_view.xml', 'wizard/crm_merge_opportunities_view.xml', 'crm_view.xml', diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index fc3cb3a3d2e..3f6e97fa186 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -2,10 +2,16 @@ <openerp> <data> - <menuitem icon="terp-partner" id="base.menu_base_partner" name="Sales" sequence="1" + <!-- Top menu item --> + <!-- + This menu item's purpose is to overwrite another one defined in + the base module in order to set new groups. + --> + <menuitem name="Sales" + id="base.menu_base_partner" groups="base.group_sale_manager,base.group_sale_salesman"/> - <menuitem id="base.menu_crm_config_lead" name="Leads & Opportunities" + <menuitem id="base.menu_crm_config_lead" name="Leads & Opportunities" parent="base.menu_base_config" sequence="80" groups="base.group_sale_manager"/> <menuitem id="base.menu_crm_config_opportunity" name="Opportunities" diff --git a/addons/crm/security/ir.model.access.csv b/addons/crm/security/ir.model.access.csv index 5665839f365..91e5f1c1769 100644 --- a/addons/crm/security/ir.model.access.csv +++ b/addons/crm/security/ir.model.access.csv @@ -24,13 +24,11 @@ access_crm_phonecall_report_user,crm.phonecall.report.user,model_crm_phonecall_r access_crm_phonecall_report_manager,crm.phonecall.report,model_crm_phonecall_report,base.group_sale_manager,1,1,1,1
access_res_partner_manager,res.partner.crm.manager,base.model_res_partner,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_mail_message_manager,mail.message.manager,mail.model_mail_message,base.group_sale_manager,1,0,0,0
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_category,res.partner.category.crm.user,base.model_res_partner_category,base.group_sale_salesman,1,1,1,0
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/test/process/lead2opportunity2win.yml b/addons/crm/test/process/lead2opportunity2win.yml index c1caf394c2e..a3e7fac372a 100644 --- a/addons/crm/test/process/lead2opportunity2win.yml +++ b/addons/crm/test/process/lead2opportunity2win.yml @@ -54,10 +54,8 @@ - After communicated with customer, I put some notes with contract details. - - !python {model: crm.add.note}: | - context.update({'active_model': 'crm.lead', 'active_id': ref('crm_case_qrecorp0')}) - note_id = self.create(cr, uid, {'body': "ces détails envoyés par le client sur le FAX pour la qualité"}) - self.action_add(cr, uid, [note_id], context=context) + !python {model: crm.lead}: | + self.message_append_note(cr, uid, [ref('crm_case_qrecorp0')], subject='Test note', body='ces détails envoyés par le client sur le FAX pour la qualité') - I win this opportunity - @@ -113,7 +111,7 @@ - !python {model: crm.meeting}: | context.update({'active_model': 'crm.meeting'}) - self.case_open(cr, uid, [ref('crm.crm_case_reviewneeds0')]) + self.case_open(cr, uid, [ref('base_calendar.crm_case_reviewneeds0')]) - I invite a user for meeting. - diff --git a/addons/crm/wizard/__init__.py b/addons/crm/wizard/__init__.py index 9d128492422..15ca8df670d 100644 --- a/addons/crm/wizard/__init__.py +++ b/addons/crm/wizard/__init__.py @@ -19,8 +19,6 @@ # ############################################################################## -import crm_add_note - import crm_lead_to_partner import crm_lead_to_opportunity import crm_phonecall_to_phonecall diff --git a/addons/crm/wizard/crm_add_note.py b/addons/crm/wizard/crm_add_note.py deleted file mode 100644 index 02f592f0136..00000000000 --- a/addons/crm/wizard/crm_add_note.py +++ /dev/null @@ -1,53 +0,0 @@ -from .. import crm -from osv import fields, osv -from tools.translate import _ -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' - _description = "Add Internal Note" - - _columns = { - 'body': fields.text('Note Body', required=True), - 'state': fields.selection(AVAILABLE_STATES, string='Set New State To', - required=True), - } - - _defaults = { - 'state': 'unchanged' - } - - def action_add(self, cr, uid, ids, context=None): - if context is None: - context = {} - - if not context.get('active_model'): - raise osv.except_osv(_('Error'), _('Can not add note!')) - - model = context.get('active_model') - case_pool = self.pool.get(model) - - for obj in self.browse(cr, uid, ids, context=context): - case_list = case_pool.browse(cr, uid, context['active_ids'], - context=context) - case = case_list[0] - case_pool.message_append(cr, uid, [case], truncate_text(obj.body), - body_text=obj.body) - 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'} - -crm_add_note() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/wizard/crm_add_note_view.xml b/addons/crm/wizard/crm_add_note_view.xml deleted file mode 100644 index fc4b57f85dc..00000000000 --- a/addons/crm/wizard/crm_add_note_view.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0"?> -<openerp> - <data> - -<!-- Add New Note view --> - - <record model="ir.ui.view" id="crm_add_new_note_view"> - <field name="name">crm.new.add.note.form</field> - <field name="model">crm.add.note</field> - <field name="type">form</field> - <field name="arch" type="xml"> - <form string="Add Note" version="7.0"> - <group> - <separator string="Add Note"/> - <field name="body"/> - </group> - <footer> - <button name="action_add" type="object" string="_Add" class="oe_highlight"/> - or - <button string="Cancel" class="oe_link" special="cancel" /> - </footer> - </form> - </field> - </record> - -<!-- Add New Note action --> - - <record model="ir.actions.act_window" id="action_crm_add_note"> - <field name="name">Add Note</field> - <field name="res_model">crm.add.note</field> - <field name="view_type">form</field> - <field name="view_mode">form</field> - <field name="view_id" ref="crm_add_new_note_view"/> - <field name="target">new</field> - </record> - - </data> -</openerp> - diff --git a/addons/crm_helpdesk/i18n/lv.po b/addons/crm_helpdesk/i18n/lv.po new file mode 100644 index 00000000000..1b9d1a4e917 --- /dev/null +++ b/addons/crm_helpdesk/i18n/lv.po @@ -0,0 +1,738 @@ +# Latvian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-07-20 08:02+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Latvian <lv@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" + +#. module: crm_helpdesk +#: field:crm.helpdesk.report,delay_close:0 +msgid "Delay to Close" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk.report,nbr:0 +msgid "# of Cases" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: view:crm.helpdesk.report:0 +msgid "Group By..." +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Today" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "March" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Helpdesk requests occurred in current year" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,company_id:0 +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,company_id:0 +msgid "Company" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,email_cc:0 +msgid "Watchers Emails" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk,priority:0 +#: selection:crm.helpdesk.report,priority:0 +msgid "Highest" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,day:0 +msgid "Day" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Add Internal Note" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Date of helpdesk requests" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Notes" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,message_ids:0 +msgid "Messages" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "My company" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk,state:0 +#: selection:crm.helpdesk.report,state:0 +msgid "Cancelled" +msgstr "" + +#. module: crm_helpdesk +#: model:ir.actions.act_window,name:crm_helpdesk.action_report_crm_helpdesk +#: model:ir.ui.menu,name:crm_helpdesk.menu_report_crm_helpdesks_tree +msgid "Helpdesk Analysis" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,date_closed:0 +msgid "Close Date" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,ref:0 +msgid "Reference" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,date_action_next:0 +msgid "Next Action" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Helpdesk Supports" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Extra Info" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: field:crm.helpdesk,partner_id:0 +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,partner_id:0 +msgid "Partner" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Estimates" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk.report,section_id:0 +msgid "Section" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Helpdesk requests occurred in last month" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Send New Email" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Helpdesk requests during last 7 days" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: selection:crm.helpdesk,state:0 +#: view:crm.helpdesk.report:0 +msgid "New" +msgstr "" + +#. module: crm_helpdesk +#: model:ir.model,name:crm_helpdesk.model_crm_helpdesk_report +msgid "Helpdesk report after Sales Services" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,email_from:0 +msgid "Email" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,channel_id:0 +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,channel_id:0 +msgid "Channel" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk,priority:0 +#: selection:crm.helpdesk.report,priority:0 +msgid "Lowest" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "# Mails" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: view:crm.helpdesk.report:0 +msgid "My Sales Team(s)" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,create_date:0 +#: field:crm.helpdesk.report,create_date:0 +msgid "Creation Date" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Reset to Draft" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: selection:crm.helpdesk,state:0 +#: selection:crm.helpdesk.report,state:0 +msgid "Pending" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: field:crm.helpdesk,date_deadline:0 +#: field:crm.helpdesk.report,date_deadline:0 +msgid "Deadline" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "July" +msgstr "" + +#. module: crm_helpdesk +#: model:ir.actions.act_window,name:crm_helpdesk.crm_helpdesk_categ_action +msgid "Helpdesk Categories" +msgstr "" + +#. module: crm_helpdesk +#: model:ir.ui.menu,name:crm_helpdesk.menu_crm_case_helpdesk-act +msgid "Categories" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "New Helpdesk Request" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "History Information" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Dates" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Month of helpdesk requests" +msgstr "" + +#. module: crm_helpdesk +#: code:addons/crm_helpdesk/crm_helpdesk.py:101 +#, python-format +msgid "No Subject" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "#Helpdesk" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "All pending Helpdesk Request" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Year of helpdesk requests" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "References" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "September" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Communication" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,month:0 +msgid "Month" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Escalate" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,write_date:0 +msgid "Update Date" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Helpdesk requests occurred in current month" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Salesman" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,ref2:0 +msgid "Reference 2" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,categ_id:0 +#: field:crm.helpdesk.report,categ_id:0 +msgid "Category" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Responsible User" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Helpdesk Support" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,planned_cost:0 +#: field:crm.helpdesk.report,planned_cost:0 +msgid "Planned Costs" +msgstr "" + +#. module: crm_helpdesk +#: help:crm.helpdesk,channel_id:0 +msgid "Communication channel." +msgstr "" + +#. module: crm_helpdesk +#: help:crm.helpdesk,email_cc:0 +msgid "" +"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" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Search Helpdesk" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,state:0 +msgid "Draft" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk,priority:0 +#: selection:crm.helpdesk.report,priority:0 +msgid "Low" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,date_closed:0 +#: selection:crm.helpdesk,state:0 +#: view:crm.helpdesk.report:0 +#: selection:crm.helpdesk.report,state:0 +msgid "Closed" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Reply" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "7 Days" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Communication & History" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "August" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk,priority:0 +#: selection:crm.helpdesk.report,priority:0 +msgid "Normal" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Global CC" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "June" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,planned_revenue:0 +msgid "Planned Revenue" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk.report,user_id:0 +msgid "User" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,active:0 +msgid "Active" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "November" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Extended Filters..." +msgstr "" + +#. module: crm_helpdesk +#: model:ir.actions.act_window,name:crm_helpdesk.crm_case_helpdesk_act111 +msgid "Helpdesk Requests" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Search" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "October" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "January" +msgstr "" + +#. module: crm_helpdesk +#: help:crm.helpdesk,email_from:0 +msgid "These people will receive email." +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: field:crm.helpdesk,date:0 +msgid "Date" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "History" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: field:crm.helpdesk,priority:0 +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,priority:0 +msgid "Priority" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,partner_address_id:0 +msgid "Partner Contact" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Misc" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: field:crm.helpdesk,state:0 +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,state:0 +msgid "State" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "General" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Send Reminder" +msgstr "" + +#. module: crm_helpdesk +#: help:crm.helpdesk,section_id:0 +msgid "" +"Sales team to which Case belongs to. Define " +"Responsible user and Email account for mail gateway." +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Done" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "December" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Cancel" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Close" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: view:crm.helpdesk.report:0 +#: selection:crm.helpdesk.report,state:0 +msgid "Open" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Helpdesk Support Tree" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk,state:0 +msgid "In Progress" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Categorization" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +#: model:ir.model,name:crm_helpdesk.model_crm_helpdesk +#: model:ir.ui.menu,name:crm_helpdesk.menu_config_helpdesk +msgid "Helpdesk" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: field:crm.helpdesk,user_id:0 +msgid "Responsible" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk.report,delay_expected:0 +msgid "Overpassed Deadline" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,description:0 +msgid "Description" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "May" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,probability:0 +msgid "Probability (%)" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk.report,email:0 +msgid "# Emails" +msgstr "" + +#. module: crm_helpdesk +#: model:ir.actions.act_window,help:crm_helpdesk.action_report_crm_helpdesk +msgid "" +"Have a general overview of all support requests by sorting them with " +"specific criteria such as the processing time, number of requests answered, " +"emails sent and costs." +msgstr "" + +#. module: crm_helpdesk +#: help:crm.helpdesk,state:0 +msgid "" +"The state is set to 'Draft', when a case is created. " +" \n" +"If the case is in progress the state is set to 'Open'. " +" \n" +"When the case is over, the state is set to 'Done'. " +" \n" +"If the case needs to be reviewed then the state is set to 'Pending'." +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "February" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,name:0 +msgid "Name" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "Month-1" +msgstr "" + +#. module: crm_helpdesk +#: model:ir.ui.menu,name:crm_helpdesk.menu_help_support_main +msgid "Helpdesk and Support" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk.report,month:0 +msgid "April" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +msgid "My Case(s)" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Query" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,id:0 +msgid "ID" +msgstr "" + +#. module: crm_helpdesk +#: model:ir.actions.act_window,help:crm_helpdesk.crm_helpdesk_categ_action +msgid "" +"Create and manage helpdesk categories to better manage and classify your " +"support requests." +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Todays's Helpdesk Requests" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Request Date" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +msgid "Open Helpdesk Request" +msgstr "" + +#. module: crm_helpdesk +#: selection:crm.helpdesk,priority:0 +#: selection:crm.helpdesk.report,priority:0 +msgid "High" +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk:0 +#: field:crm.helpdesk,section_id:0 +#: view:crm.helpdesk.report:0 +msgid "Sales Team" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,date_action_last:0 +msgid "Last Action" +msgstr "" + +#. module: crm_helpdesk +#: model:ir.actions.act_window,help:crm_helpdesk.crm_case_helpdesk_act111 +msgid "" +"Helpdesk and Support allow you to track your interventions. Select a " +"customer, add notes and categorize interventions with partners if necessary. " +"You can also assign a priority level. Use the OpenERP Issues system to " +"manage your support activities. Issues can be connected to the email " +"gateway: new emails may create issues, each of them automatically gets the " +"history of the conversation with the customer." +msgstr "" + +#. module: crm_helpdesk +#: view:crm.helpdesk.report:0 +#: field:crm.helpdesk.report,name:0 +msgid "Year" +msgstr "" + +#. module: crm_helpdesk +#: field:crm.helpdesk,duration:0 +msgid "Duration" +msgstr "" diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index c4ed2eedb85..bf57342f14f 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -101,6 +101,11 @@ class email_template(osv.osv): mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model return {'value':{'model': mod_name}} + def name_get(self, cr, uid, ids, context=None): + """ Override name_get of mail.message: return directly the template + name, and not the generated name from mail.message.common.""" + return [(record.id, record.name) for record in self.browse(cr, uid, ids, context=context)] + _columns = { 'name': fields.char('Name', size=250), 'model_id': fields.many2one('ir.model', 'Related document model'), @@ -311,7 +316,7 @@ class email_template(osv.osv): 'attachment_ids': False, 'message_id': False, 'state': 'outgoing', - 'subtype': 'plain', + 'content_subtype': 'plain', } if not template_id: return values @@ -326,8 +331,15 @@ class email_template(osv.osv): template.model, res_id, context=context) \ or False + # if email_to: find or create a partner + if values['email_to']: + partner_id = self.pool.get('mail.thread').message_partner_by_email(cr, uid, values['email_to'], context=context)['partner_id'] + if not partner_id: + partner_id = self.pool.get('res.partner').name_create(cr, uid, values['email_to'], context=context) + values['partner_ids'] = [partner_id] + if values['body_html']: - values.update(subtype='html') + values.update(content_subtype='html') if template.user_signature: signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature diff --git a/addons/email_template/static/src/img/email_template.png b/addons/email_template/static/src/img/email_template.png Binary files differnew file mode 100644 index 00000000000..d679492bc19 --- /dev/null +++ b/addons/email_template/static/src/img/email_template.png diff --git a/addons/email_template/static/src/img/email_template_save.png b/addons/email_template/static/src/img/email_template_save.png Binary files differnew file mode 100644 index 00000000000..bf29f9ab4ad --- /dev/null +++ b/addons/email_template/static/src/img/email_template_save.png diff --git a/addons/email_template/wizard/email_compose_message_view.xml b/addons/email_template/wizard/email_compose_message_view.xml index 96ea55da9e5..a911034cb1a 100644 --- a/addons/email_template/wizard/email_compose_message_view.xml +++ b/addons/email_template/wizard/email_compose_message_view.xml @@ -25,5 +25,32 @@ </data> </field> </record> + + <record model="ir.ui.view" id="email_compose_message_wizard_inherit_form_chatter"> + <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_chatter"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//field[@name='dest_partner_ids']" position="after"> + <field name="use_template" colspan="2" nolabel="1" invisible="1" + on_change="onchange_use_template(use_template, context)"/> + <field name="template_id" colspan="2" nolabel="1" + attrs="{'invisible':[('use_template','=',False)]}" + on_change="on_change_template(use_template, template_id, False, False, context)"/> + </xpath> + <xpath expr="//a[@class='oe_mail_compose_message_checklist']" position="before"> + <button icon="../../../../../email_template/static/src/img/email_template" + type="object" name="template_toggle" string="" + help="Use a message template"/> + <button icon="../../../../../email_template/static/src/img/email_template_save" + type="object" name="save_as_template" string="" + help="Save as a new template"/> + </xpath> + </data> + </field> + </record> + </data> </openerp> diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py index a4fd749979c..1b007a990c0 100644 --- a/addons/email_template/wizard/mail_compose_message.py +++ b/addons/email_template/wizard/mail_compose_message.py @@ -27,19 +27,6 @@ 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 - 'context': {'mail.compose.target.model': model} - } - class mail_compose_message(osv.osv_memory): _inherit = 'mail.compose.message' @@ -87,6 +74,8 @@ class mail_compose_message(osv.osv_memory): else: # render the mail as one-shot values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context) + # get partner_ids back + values['dest_partner_ids'] = values['partner_ids'] # retrofit generated attachments in the expected field format if values['attachments']: attachment = values.pop('attachments') @@ -110,18 +99,24 @@ class mail_compose_message(osv.osv_memory): 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: + values = {} + use_template = record.use_template + # simulate an on_change on use_template + values.update(self.onchange_use_template(cr, uid, ids, not use_template, context=context)['value']) + record.write(values) + return False + + def onchange_use_template(self, cr, uid, ids, use_template, context=None): + values = {'use_template': use_template} + for record in self.browse(cr, uid, ids, context=context): + if not use_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) + onchange_template_values = self.on_change_template(cr, uid, record.id, use_template, + False, email_from=record.email_from, email_to=record.email_to, context=context) + values.update(onchange_template_values['value']) + return {'value': values} def save_as_template(self, cr, uid, ids, context=None): if context is None: @@ -153,7 +148,7 @@ class mail_compose_message(osv.osv_memory): 'use_template': True}) # _reopen same wizard screen with new template preselected - return _reopen(self, record.id, model) + return False # override the basic implementation def render_template(self, cr, uid, template, model, res_id, context=None): diff --git a/addons/event/__openerp__.py b/addons/event/__openerp__.py index 1a7f3ba09c5..feb77249cbb 100644 --- a/addons/event/__openerp__.py +++ b/addons/event/__openerp__.py @@ -25,6 +25,7 @@ 'name': 'Events Organisation', 'version': '0.1', 'category': 'Tools', + 'summary': 'Invitations, Subscriptions', 'description': """ Organization and management of Events. ====================================== diff --git a/addons/hr/__openerp__.py b/addons/hr/__openerp__.py index 71d7d9aa93d..9833e2d611c 100644 --- a/addons/hr/__openerp__.py +++ b/addons/hr/__openerp__.py @@ -26,6 +26,7 @@ "category": "Human Resources", "sequence": 12, "website": "http://www.openerp.com", + "summary": "Hierarchy, Jobs, Departments", "description": """ Module for human resource management. ===================================== diff --git a/addons/hr/hr.py b/addons/hr/hr.py index 4a0c3736222..2a0ea3b45a4 100644 --- a/addons/hr/hr.py +++ b/addons/hr/hr.py @@ -212,6 +212,16 @@ class hr_employee(osv.osv): 'last_login': fields.related('user_id', 'date', type='datetime', string='Latest Connection', readonly=1), } + def create(self, cr, uid, data, context=None): + employee_id = super(hr_employee, self).create(cr, uid, data, context=context) + try: + (model, mail_group_id) = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'group_all_employees') + employee = self.browse(cr, uid, employee_id, context=context) + self.pool.get('mail.group').message_append_note(cr, uid, [mail_group_id], body='Welcome to %s! Please help him make its first steps in OpenERP!' % (employee.name), context=context) + except: + pass # group deleted: do not push a message + return employee_id + def unlink(self, cr, uid, ids, context=None): resource_obj = self.pool.get('resource.resource') resource_ids = [] diff --git a/addons/hr_evaluation/__openerp__.py b/addons/hr_evaluation/__openerp__.py index 231f4593868..fbb1bcb162f 100644 --- a/addons/hr_evaluation/__openerp__.py +++ b/addons/hr_evaluation/__openerp__.py @@ -25,6 +25,7 @@ "author": "OpenERP SA", "category": "Human Resources", "website": "http://www.openerp.com", + "summary": "Top-Down and Bottom-Up Evaluation", "images": ["images/hr_evaluation_analysis.jpeg","images/hr_evaluation.jpeg"], "depends": ["hr","base_calendar","survey"], "description": """ diff --git a/addons/hr_evaluation/wizard/mail_compose_message.py b/addons/hr_evaluation/wizard/mail_compose_message.py index 7b4c1b69164..7e4ba326947 100644 --- a/addons/hr_evaluation/wizard/mail_compose_message.py +++ b/addons/hr_evaluation/wizard/mail_compose_message.py @@ -44,15 +44,9 @@ class mail_compose_message(osv.osv_memory): 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 diff --git a/addons/hr_expense/__openerp__.py b/addons/hr_expense/__openerp__.py index 2a1d302e094..c40670508f4 100644 --- a/addons/hr_expense/__openerp__.py +++ b/addons/hr_expense/__openerp__.py @@ -25,6 +25,7 @@ 'version': '1.0', 'category': 'Human Resources', "sequence": 30, + "summary": "Expenses Validation, Invoicing", 'description': """ This module aims to manage employee's expenses. =============================================== diff --git a/addons/hr_holidays/__openerp__.py b/addons/hr_holidays/__openerp__.py index 4509508b2be..b37dd3c7859 100644 --- a/addons/hr_holidays/__openerp__.py +++ b/addons/hr_holidays/__openerp__.py @@ -26,6 +26,7 @@ "author": "OpenERP SA", "category": "Human Resources", "sequence": 28, + "summary": "Allocation and Leave Requests, Reporting by Department", "website": "http://www.openerp.com", "description": """ This module allows you to manage leaves and leaves' requests. diff --git a/addons/hr_recruitment/__openerp__.py b/addons/hr_recruitment/__openerp__.py index e5df8cf2bf3..fe6c86c684d 100644 --- a/addons/hr_recruitment/__openerp__.py +++ b/addons/hr_recruitment/__openerp__.py @@ -24,6 +24,7 @@ 'version': '1.0', 'category': 'Human Resources', "sequence": 24, + "summary": "Job Descriptions, Recruitment Process", 'description': """ Manages job positions and the recruitment process. ================================================== diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml index fe5d3d9969b..6b64964eb2a 100644 --- a/addons/hr_recruitment/hr_recruitment_data.xml +++ b/addons/hr_recruitment/hr_recruitment_data.xml @@ -1,9 +1,6 @@ <?xml version="1.0"?> <openerp> -<!-- <data noupdate="1"> ---> -<data> <!-- Meeting Types (for interview meetings) --> <record model="crm.meeting.type" id="categ_meet_interview"> diff --git a/addons/hr_recruitment/security/ir.model.access.csv b/addons/hr_recruitment/security/ir.model.access.csv index 7d082070be5..8b029c9324a 100644 --- a/addons/hr_recruitment/security/ir.model.access.csv +++ b/addons/hr_recruitment/security/ir.model.access.csv @@ -3,7 +3,6 @@ access_hr_applicant_user,hr.applicant.user,model_hr_applicant,base.group_hr_user 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_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_survey_hr_user,survey.hr.user,survey.model_survey,base.group_hr_user,1,1,1,0
access_crm_meeting_hruser,crm.meeting.hruser,base_calendar.model_crm_meeting,base.group_hr_user,1,1,1,1
diff --git a/addons/hr_timesheet_sheet/__openerp__.py b/addons/hr_timesheet_sheet/__openerp__.py index defa3ee5873..04be6045f4d 100644 --- a/addons/hr_timesheet_sheet/__openerp__.py +++ b/addons/hr_timesheet_sheet/__openerp__.py @@ -25,6 +25,7 @@ 'version': '1.0', 'category': 'Human Resources', "sequence": 16, + "summary": "Attendances, Activities, Timing", 'description': """ This module helps you to easily encode and validate timesheet and attendances within the same view. =================================================================================================== diff --git a/addons/import_google/i18n/es.po b/addons/import_google/i18n/es.po new file mode 100644 index 00000000000..325df15688c --- /dev/null +++ b/addons/import_google/i18n/es.po @@ -0,0 +1,217 @@ +# Spanish translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-07-21 21:08+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish <es@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-07-22 04:45+0000\n" +"X-Generator: Launchpad (build 15654)\n" + +#. module: import_google +#: help:synchronize.google.import,group_name:0 +msgid "Choose which group to import, By default it takes all." +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import Google Calendar Events" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Import Events" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:71 +#, python-format +msgid "" +"No Google Username or password Defined for user.\n" +"Please define in user view" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:127 +#, python-format +msgid "" +"Invalid login detail !\n" +" Specify Username/Password." +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,supplier:0 +msgid "Supplier" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import Options" +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,group_name:0 +msgid "Group Name" +msgstr "" + +#. module: import_google +#: model:ir.model,name:import_google.model_crm_case_categ +msgid "Category of Case" +msgstr "" + +#. module: import_google +#: model:ir.actions.act_window,name:import_google.act_google_login_contact_form +#: model:ir.ui.menu,name:import_google.menu_sync_contact +msgid "Import Google Contacts" +msgstr "" + +#. module: import_google +#: view:google.import.message:0 +msgid "Import Google Data" +msgstr "" + +#. module: import_google +#: view:crm.meeting:0 +msgid "Meeting Type" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google.py:38 +#: code:addons/import_google/wizard/import_google_data.py:28 +#, python-format +msgid "" +"Please install gdata-python-client from http://code.google.com/p/gdata-" +"python-client/downloads/list" +msgstr "" + +#. module: import_google +#: model:ir.model,name:import_google.model_google_login +msgid "Google Contact" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import contacts from a google account" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:133 +#, python-format +msgid "Please specify correct user and password !" +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,customer:0 +msgid "Customer" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Cancel" +msgstr "" + +#. module: import_google +#: model:ir.model,name:import_google.model_synchronize_google_import +msgid "synchronize.google.import" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Import Contacts" +msgstr "" + +#. module: import_google +#: model:ir.actions.act_window,name:import_google.act_google_login_form +#: model:ir.ui.menu,name:import_google.menu_sync_calendar +msgid "Import Google Calendar" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:50 +#, python-format +msgid "Import google" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:127 +#: code:addons/import_google/wizard/import_google_data.py:133 +#, python-format +msgid "Error" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:71 +#, python-format +msgid "Warning !" +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,create_partner:0 +msgid "Options" +msgstr "" + +#. module: import_google +#: view:google.import.message:0 +msgid "_Ok" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google.py:38 +#: code:addons/import_google/wizard/import_google_data.py:28 +#, python-format +msgid "Google Contacts Import Error!" +msgstr "" + +#. module: import_google +#: model:ir.model,name:import_google.model_google_import_message +msgid "Import Message" +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,calendar_name:0 +msgid "Calendar Name" +msgstr "" + +#. module: import_google +#: help:synchronize.google.import,supplier:0 +msgid "Check this box to set newly created partner as Supplier." +msgstr "" + +#. module: import_google +#: selection:synchronize.google.import,create_partner:0 +msgid "Import only address" +msgstr "" + +#. module: import_google +#: field:crm.case.categ,user_id:0 +msgid "User" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Partner Status for this Group:" +msgstr "" + +#. module: import_google +#: field:google.import.message,name:0 +msgid "Message" +msgstr "" + +#. module: import_google +#: selection:synchronize.google.import,create_partner:0 +msgid "Create partner for each contact" +msgstr "" + +#. module: import_google +#: help:synchronize.google.import,customer:0 +msgid "Check this box to set newly created partner as Customer." +msgstr "" diff --git a/addons/l10n_be_invoice_bba/partner_view.xml b/addons/l10n_be_invoice_bba/partner_view.xml index 8be0f31d612..d78df2d7a42 100644 --- a/addons/l10n_be_invoice_bba/partner_view.xml +++ b/addons/l10n_be_invoice_bba/partner_view.xml @@ -9,7 +9,7 @@ <field name="type">form</field> <field name="inherit_id" ref="base.view_partner_form"/> <field name="arch" type="xml"> - <field name="property_payment_term" position="after"> + <field name="last_reconciliation_date" position="after"> <field name="out_inv_comm_type"/> <field name="out_inv_comm_algorithm" attrs="{'invisible':[('out_inv_comm_type','!=','bba')]}"/> </field> diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index e911f24e809..abb290332a7 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -24,6 +24,7 @@ 'version': '1.0', 'category':'Social Network', "sequence": 2, + "summary": "Discussions, Feeds, Alerts", 'description': """ A bussiness oriented Social Networking with a fully-integrated email and message management. @@ -66,11 +67,11 @@ The main features of the module are : 'mail_thread_view.xml', 'mail_group_view.xml', 'res_partner_view.xml', + 'res_users_view.xml', + 'data/mail_data.xml', + 'data/mail_group_data.xml', 'security/mail_security.xml', 'security/ir.model.access.csv', - 'mail_data.xml', - 'mail_group_data.xml', - 'res_users_view.xml', ], 'installable': True, 'auto_install': False, @@ -83,13 +84,15 @@ The main features of the module are : 'static/src/img/email_icong.png', 'static/src/img/_al.png', 'static/src/img/_pincky.png', - 'static/src/img/feeds.png', - 'static/src/img/feeds-hover.png', 'static/src/img/groupdefault.png', + 'static/src/img/attachment.png', + 'static/src/img/checklist.png', + 'static/src/img/formatting.png', ], 'css': [ 'static/src/css/mail.css', 'static/src/css/mail_group.css', + 'static/src/css/mail_compose_message.css', ], 'js': [ 'static/lib/jquery.expander/jquery.expander.js', @@ -99,7 +102,7 @@ The main features of the module are : 'static/src/xml/mail.xml', ], 'demo': [ - 'mail_demo.xml', + 'data/mail_demo.xml', ], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/mail_data.xml b/addons/mail/data/mail_data.xml index c8e8b1a488e..c8e8b1a488e 100644 --- a/addons/mail/mail_data.xml +++ b/addons/mail/data/mail_data.xml diff --git a/addons/mail/mail_demo.xml b/addons/mail/data/mail_demo.xml index 1adec42a910..274259e544e 100644 --- a/addons/mail/mail_demo.xml +++ b/addons/mail/data/mail_demo.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> <openerp> - <data> + <data noupdate="1"> <record id="message_blogpost0_attachment0" model="ir.attachment"> <field name="name">A cool attachment</field> @@ -28,7 +28,7 @@ <field name="subject">Internal company announce</field> <field name="model">mail.group</field> <field name="res_id" ref="group_all_company"/> - <field name="subtype">html</field> + <field name="content_subtype">html</field> <field name="body_html"><![CDATA[Lorem ipsum dolor <b>sit amet</b>, consectetur <em>adipiscing elit</em>. Pellentesque et quam sapien, in sagittis tellus. Praesent vel massa sed massa consequat egestas in tristique orci. Praesent iaculis libero et neque vehicula iaculis. Vivamus placerat tincidunt orci ac ornare. Proin ut dolor fringilla velit ultricies consequat. Maecenas sit amet ipsum non leo interdum imperdiet. Donec sapien mi, varius a consequat id, consectetur sit amet nulla. @@ -41,10 +41,9 @@ Nulla turpis leo, rhoncus ut egestas sit amet, consectetur vitae urna. Mauris in </record> <record id="message_blogpost0_comment0" model="mail.message"> - <field name="subject">Reply</field> <field name="model">mail.group</field> <field name="res_id" ref="group_all_company"/> - <field name="subtype">html</field> + <field name="content_subtype">html</field> <field name="body_html"><![CDATA[That was such a <b>tremendous</b> blogpost ! (first comment)]]></field> <field name="parent_id" ref="message_blogpost0"/> <field name="type">comment</field> @@ -52,10 +51,9 @@ Nulla turpis leo, rhoncus ut egestas sit amet, consectetur vitae urna. Mauris in </record> <record id="message_blogpost0_comment1" model="mail.message"> - <field name="subject">Reply</field> <field name="model">mail.group</field> <field name="res_id" ref="group_all_company"/> - <field name="subtype">html</field> + <field name="content_subtype">html</field> <field name="body_html"><![CDATA[Agreed ! Would it be possible to learn more about the author ? (second comment)]]></field> <field name="parent_id" ref="message_blogpost0"/> @@ -75,10 +73,9 @@ Would it be possible to learn more about the author ? (second comment)]]></fiel </record> <record id="message_blogpost0_comment2" model="mail.message"> - <field name="subject">Reply</field> <field name="model">mail.group</field> <field name="res_id" ref="group_all_company"/> - <field name="subtype">html</field> + <field name="content_subtype">html</field> <field name="body_html"><![CDATA[Sure: Curabitur tempor bibendum diam, et euismod ante rutrum vel. In quis purus neque. Integer sodales dolor eu elit fringilla blandit. Maecenas lacus nisi, facilisis sit amet viverra eu, tristique vel augue. diff --git a/addons/mail/mail_group_data.xml b/addons/mail/data/mail_group_data.xml index c2d1762d6ae..11c259b3ee8 100644 --- a/addons/mail/mail_group_data.xml +++ b/addons/mail/data/mail_group_data.xml @@ -4,10 +4,15 @@ <record model="mail.group" id="group_all_company"> <field name="name">All Company</field> + <field name="description">All company users can come here and discuss.</field> </record> <record model="mail.group" id="group_sales"> <field name="name">Sales</field> </record> + <record model="mail.group" id="group_all_employees"> + <field name="name">All Employees</field> + <field name="group_ids" eval="[(4, ref('base.group_user'))]"/> + </record> </data> </openerp> diff --git a/addons/mail/doc/mail_message.rst b/addons/mail/doc/mail_message.rst index e41a772cac8..56244e251bf 100644 --- a/addons/mail/doc/mail_message.rst +++ b/addons/mail/doc/mail_message.rst @@ -3,22 +3,26 @@ mail.message ============ -TODO +Models ++++++++ -mail.group -++++++++++ +``mail.message.common`` is an 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. -A mail_group is a collection of users sharing messages in a discussion group. Group users are users that follow the mail group, using the subscription/follow mechanism of OpenSocial. A mail group has nothing in common wih res.users.group. -Additional information on fields: +All internal logic should be in a database-based model while this model +holds the basics of a message. For example, a wizard for writing emails +should inherit from this class and not from mail.message. - - ``member_ids``: user member of the groups are calculated with ``message_get_subscribers`` method from mail.thread - - ``member_count``: calculated with member_ids - - ``is_subscriber``: calculated with member_ids -res.users -+++++++++ +.. versionchanged:: 7.0 -OpenChatter updates the res.users class: - - it adds a preference about sending emails when receiving a notification - - make a new user follow itself automatically - - create a welcome message when creating a new user, to make his arrival in OpenERP more friendly + - ``subtype`` is renamed to ``content_subtype``: usually 'html' or 'plain'. + This field is used to select plain-text or rich-text contents accordingly. + - ``subtype`` is moved to mail.message model. The purpose is to be able to + distinguish message of the same type, such as notifications about creating + or cancelling a record. For example, it is used to add the possibility + to hide notifications in the wall. + +Those changes aim at being able to distinguish the message content to the +message itself. diff --git a/addons/mail/doc/mail_thread.rst b/addons/mail/doc/mail_thread.rst index f15f78156d3..41ea014b06c 100644 --- a/addons/mail/doc/mail_thread.rst +++ b/addons/mail/doc/mail_thread.rst @@ -3,7 +3,25 @@ mail.thread and OpenChatter =========================== -TODO +API ++++ + +Writing messages and notifications +---------------------------------- + +``message_append`` + +Creates a new mail.message through message_create. The new message is 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. + +This method calls message_create that will handle management of subscription +and notifications, and effectively create the message. + +If ``email_from`` is not set or ``type`` not set as 'email', a note message +is created (comment or system notification), without the usual envelope +attributes (sender, recipients, etc.). mail.group ++++++++++ diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index f691d75e0bc..36001ee6074 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -36,7 +36,7 @@ class mail_group(osv.osv): A mail_group is a collection of users sharing messages in a discussion group. Group users are users that follow the mail group, using the subscription/follow mechanism of OpenSocial. A mail group has nothing - in common wih res.users.group. + in common with res.users.group. Additional information on fields: - ``member_ids``: user member of the groups are calculated with ``message_get_subscribers`` method from mail.thread @@ -49,12 +49,6 @@ class mail_group(osv.osv): _name = 'mail.group' _inherit = ['mail.thread'] - def action_group_join(self, cr, uid, ids, context={}): - return self.message_subscribe(cr, uid, ids, context=context); - - def action_group_leave(self, cr, uid, ids, context={}): - return self.message_unsubscribe(cr, uid, ids, context=context); - def onchange_photo(self, cr, uid, ids, value, context=None): if not value: return {'value': {'avatar_big': value, 'avatar': value} } @@ -105,7 +99,7 @@ class mail_group(osv.osv): message_obj = self.pool.get('mail.message') for id in ids: lower_date = (DT.datetime.now() - DT.timedelta(days=30)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) - result[id] = message_obj.search(cr, uid, ['&', '&', ('model', '=', self._name), ('res_id', 'in', ids), ('date', '>=', lower_date)], count=True, context=context) + result[id] = self.message_search(cr, uid, [id], limit=None, domain=[('date', '>=', lower_date)], count=True, context=context) return result def _get_default_photo(self, cr, uid, context=None): @@ -116,19 +110,33 @@ class mail_group(osv.osv): 'name': fields.char('Name', size=64, required=True), 'description': fields.text('Description'), 'responsible_id': fields.many2one('res.users', string='Responsible', - ondelete='set null', required=True, select=1, - help="Responsible of the group that has all rights on the record."), - 'public': fields.boolean('Public', help='This group is visible by non members. Invisible groups can add members through the invite button.'), - 'photo_big': fields.binary('Full-size photo', help='Field holding the full-sized PIL-supported and base64 encoded version of the group image. The photo field is used as an interface for this field.'), - 'photo': fields.function(_get_photo, fnct_inv=_set_photo, string='Photo', type="binary", + ondelete='set null', required=True, select=1, + help="Responsible of the group that has all rights on the record."), + 'public': fields.boolean('Visible by non members', help='This group is visible by non members. \ + Invisible groups can add members through the invite button.'), + 'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel', + id1='mail_group_id', id2='groups_id', string='Linked groups', + help="Members of those groups will automatically added as followers. "\ + "Note that they will be able to manage their subscription manually "\ + "if necessary."), + 'photo_big': fields.binary('Full-size photo', + help='Field holding the full-sized PIL-supported and base64 encoded "\ + version of the group image. The photo field is used as an "\ + interface for this field.'), + 'photo': fields.function(_get_photo, fnct_inv=_set_photo, + string='Photo', type="binary", store = { 'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['photo_big'], 10), - }, help='Field holding the automatically resized (128x128) PIL-supported and base64 encoded version of the group image.'), - 'member_ids': fields.function(get_member_ids, fnct_search=search_member_ids, type='many2many', - relation='res.users', string='Group members', multi='get_member_ids'), - 'member_count': fields.function(get_member_ids, type='integer', string='Member count', multi='get_member_ids'), - 'is_subscriber': fields.function(get_member_ids, type='boolean', string='Joined', multi='get_member_ids'), - 'last_month_msg_nbr': fields.function(get_last_month_msg_nbr, type='integer', string='Messages count for last month'), + }, + help='Field holding the automatically resized (128x128) PIL-supported and base64 encoded version of the group image.'), + 'member_ids': fields.function(get_member_ids, fnct_search=search_member_ids, + type='many2many', relation='res.users', string='Group members', multi='get_member_ids'), + 'member_count': fields.function(get_member_ids, type='integer', + string='Member count', multi='get_member_ids'), + 'is_subscriber': fields.function(get_member_ids, type='boolean', + string='Joined', multi='get_member_ids'), + 'last_month_msg_nbr': fields.function(get_last_month_msg_nbr, type='integer', + string='Messages count for last month'), } _defaults = { @@ -136,3 +144,32 @@ class mail_group(osv.osv): 'responsible_id': (lambda s, cr, uid, ctx: uid), 'photo': _get_default_photo, } + + def _subscribe_user_with_group_m2m_command(self, cr, uid, ids, group_ids_command, context=None): + # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} + user_group_ids = [command[1] for command in group_ids_command if command[0] == 4] + user_group_ids += [id for command in group_ids_command if command[0] == 6 for id in command[2]] + # retrieve the user member of those groups + user_ids = [] + res_groups_obj = self.pool.get('res.groups') + for group in res_groups_obj.browse(cr, uid, user_group_ids, context=context): + user_ids += [user.id for user in group.users] + # subscribe the users + return self.message_subscribe(cr, uid, ids, user_ids, context=context) + + def create(self, cr, uid, vals, context=None): + mail_group_id = super(mail_group, self).create(cr, uid, vals, context=context) + if vals.get('group_ids'): + self._subscribe_user_with_group_m2m_command(cr, uid, [mail_group_id], vals.get('group_ids'), context=context) + return mail_group_id + + def write(self, cr, uid, ids, vals, context=None): + if vals.get('group_ids'): + self._subscribe_user_with_group_m2m_command(cr, uid, ids, vals.get('group_ids'), context=context) + return super(mail_group, self).write(cr, uid, ids, vals, context=context) + + def action_group_join(self, cr, uid, ids, context=None): + return self.message_subscribe(cr, uid, ids, context=context) + + def action_group_leave(self, cr, uid, ids, context=None): + return self.message_unsubscribe(cr, uid, ids, context=context) diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index 97210b8c179..c755209f952 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -64,6 +64,7 @@ <newline/> <group colspan="4" col="4"> <field name="description" colspan="4" nolabel="1"/> + <field name="group_ids" colspan="4" widget="many2many_tags" class="oe_edit_only"/> </group> <group colspan="2" col="2" class="oe_edit_only"> <field name="responsible_id" colspan="2"/> diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 301657ba4c2..b96b6e3791e 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -30,66 +30,61 @@ import datetime from email.header import decode_header from email.message import Message -import tools +from openerp import SUPERUSER_ID from osv import osv from osv import fields +import pytz +from tools import DEFAULT_SERVER_DATETIME_FORMAT from tools.translate import _ -from openerp import SUPERUSER_ID +import tools _logger = logging.getLogger(__name__) -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 - +""" Some tools for parsing / creating email fields """ 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): +def mail_tools_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""" +# TODO: remove that after cleaning +def to_email(text): + return mail_tools_to_email(text) + +class mail_message_common(osv.TransientModel): + """ 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. + All internal logic should be in another model while this + model holds the basics of a message. For example, a wizard for writing + emails should inherit from this class and not from mail.message.""" def get_body(self, cr, uid, ids, name, arg, context=None): - if context is None: - context = {} + """ get correct body version: body_html for html messages, and + body_text for plain text messages + """ result = dict.fromkeys(ids, '') for message in self.browse(cr, uid, ids, context=context): - if message.subtype == 'html': + if message.content_subtype == 'html': result[message.id] = message.body_html else: result[message.id] = message.body_text return result def search_body(self, cr, uid, obj, name, args, context=None): - """will receive: - - obj: mail.message object - - name: 'body' - - args: [('body', 'ilike', 'blah')]""" - return ['|', '&', ('subtype', '=', 'html'), ('body_html', args[0][1], args[0][2]), ('body_text', args[0][1], args[0][2])] + # will receive: + # - obj: mail.message object + # - name: 'body' + # - args: [('body', 'ilike', 'blah')] + return ['|', '&', ('content_subtype', '=', 'html'), ('body_html', args[0][1], args[0][2]), ('body_text', args[0][1], args[0][2])] def get_record_name(self, cr, uid, ids, name, arg, context=None): - if context is None: - context = {} result = dict.fromkeys(ids, '') for message in self.browse(cr, uid, ids, context=context): if not message.model or not message.res_id: @@ -116,8 +111,9 @@ class mail_message_common(osv.osv_memory): 'subject': fields.char('Subject', size=512), 'model': fields.char('Related Document Model', size=128, select=1), 'res_id': fields.integer('Related Document ID', select=1), - 'record_name': fields.function(get_record_name, type='string', string='Message Record Name', - help="Name of the record, matching the result of the name_get."), + 'record_name': fields.function(get_record_name, type='string', + string='Message Record Name', + help="Name get of the related document."), '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'), @@ -125,104 +121,99 @@ class mail_message_common(osv.osv_memory): '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)"), + 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), + 'content_subtype': fields.char('Message content subtype', size=32, + oldname="subtype", readonly=1, + help="Type of message, usually 'html' or 'plain', used to select "\ + "plain-text or rich-text contents accordingly"), '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"), - 'body': fields.function(get_body, fnct_search = search_body, string='Message Content', type='text', - help="Content of the message. This content equals the body_text field for plain-test messages, and body_html for rich-text/HTML messages. This allows having one field if we want to access the content matching the message subtype."), - 'parent_id': fields.many2one('mail.message', 'Parent Message', help="Parent message, used for displaying as threads with hierarchy", - select=True, ondelete='set null',), - 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), + 'body': fields.function(get_body, fnct_search = search_body, type='text', + string='Message Content', store=True, + help="Content of the message. This content equals the body_text field "\ + "for plain-test messages, and body_html for rich-text/HTML "\ + "messages. This allows having one field if we want to access "\ + "the content matching the message content_subtype."), + 'parent_id': fields.many2one('mail.message.common', 'Parent Message', + select=True, ondelete='set null', + help="Parent message, used for displaying as threads with hierarchy"), } _defaults = { - 'subtype': 'plain', + 'content_subtype': 'plain', 'date': (lambda *a: fields.datetime.now()), } -class mail_message(osv.osv): - '''Model holding messages: system notification (replacing res.log - notifications), comments (for OpenSocial feature) and +class mail_message(osv.Model): + """Model holding messages: system notification (replacing res.log + notifications), comments (for OpenChatter feature) and RFC2822 email messages. This model also provides facilities to parse, queue and send new email messages. Type of messages - are differentiated using the 'type' column. - - The ``display_text`` field will have a slightly different - presentation for real emails and for log messages. - ''' + are differentiated using the 'type' column. """ _name = 'mail.message' _inherit = 'mail.message.common' _description = 'Mail Message (email, comment, notification)' _order = 'date desc' - # XXX to review - how to determine action to use? def open_document(self, cr, uid, ids, context=None): + """ Open the message related document. Note that only the document of + ids[0] will be opened. + TODO: how to determine the action to use ? + """ 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), + if not ids: + return action_data + msg = self.browse(cr, uid, ids[0], context=context) + ir_act_window = self.pool.get('ir.actions.act_window') + action_ids = ir_act_window.search(cr, uid, [('res_model', '=', msg.model)], context=context) + if action_ids: + action_data = ir_act_window.read(cr, uid, action_ids[0], context=context) + action_data.update({ + 'domain' : "[('id', '=', %d)]" % (msg.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): + """ Open the message related attachments. + TODO: how to determine the action to use ? + """ action_data = False + if not ids: + return action_data 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')]) + messages = self.browse(cr, uid, ids, context=context) + att_ids = [x.id for message in messages for x in message.attachment_ids] + action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')], context=context) if action_ids: action_data = action_pool.read(cr, uid, action_ids[0], context=context) action_data.update({ - 'domain': [('id','in',att_ids)], + '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 = {} - - # Read message as UID 1 to allow viewing author even if from different company - for message in self.browse(cr, SUPERUSER_ID, ids): - 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 or '') - result[message.id] = msg_txt - return result _columns = { 'type': fields.selection([ ('email', 'email'), ('comment', 'Comment'), ('notification', 'System notification'), - ], 'Type', help="Message type: email for email message, notification for system message, comment for other messages such as user replies"), - 'partner_id': fields.many2one('res.partner', 'Related partner'), + ], 'Type', + help="Message type: email for email message, notification for system "\ + "message, comment for other messages such as user replies"), + 'partner_id': fields.many2one('res.partner', 'Related partner', + help="Deprecated field. Use partner_ids instead."), + 'partner_ids': fields.many2many('res.partner', + 'mail_message_destination_partner_rel', + 'message_id', 'partner_id', 'Destination partners', + help="When sending emails through the social network composition wizard"\ + "you may choose to send a copy of the mail to partners."), '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'), @@ -231,8 +222,14 @@ class mail_message(osv.osv): ('exception', 'Delivery Failed'), ('cancel', 'Cancelled'), ], 'Status', 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), + 'auto_delete': fields.boolean('Auto Delete', + help="Permanently delete this email after sending it, to save space"), + 'original': fields.binary('Original', readonly=1, + help="Original version of the message, as it was sent on the network"), + 'parent_id': fields.many2one('mail.message', 'Parent Message', + select=True, ondelete='set null', + help="Parent message, used for displaying as threads with hierarchy"), + 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), } _defaults = { @@ -249,72 +246,124 @@ class mail_message(osv.osv): if not cr.fetchone(): cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""") + def check(self, cr, uid, ids, mode, context=None, values=None): + """Restricts the access to a mail.message, according to referred model + """ + if not ids: + return + res_ids = {} + if isinstance(ids, (int, long)): + ids = [ids] + cr.execute('SELECT DISTINCT model, res_id FROM mail_message WHERE id = ANY (%s)', (ids,)) + for rmod, rid in cr.fetchall(): + if not (rmod and rid): + continue + res_ids.setdefault(rmod,set()).add(rid) + if values: + if 'res_model' in values and 'res_id' in values: + res_ids.setdefault(values['res_model'],set()).add(values['res_id']) + + ima_obj = self.pool.get('ir.model.access') + for model, mids in res_ids.items(): + # ignore mail messages that are not attached to a resource anymore when checking access rights + # (resource was deleted but message was not) + mids = self.pool.get(model).exists(cr, uid, mids) + ima_obj.check(cr, uid, model, mode) + self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context) + + def create(self, cr, uid, values, context=None): + self.check(cr, uid, [], mode='create', context=context, values=values) + return super(mail_message, self).create(cr, uid, values, context) + + def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'): + self.check(cr, uid, ids, 'read', context=context) + return super(mail_message, self).read(cr, uid, ids, fields_to_read, context, load) + 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) + self.check(cr, uid, [id], 'read', context=context) + 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) - + + def write(self, cr, uid, ids, vals, context=None): + self.check(cr, uid, ids, 'write', context=context, values=vals) + return super(mail_message, self).write(cr, uid, ids, vals, context) + + def unlink(self, cr, uid, ids, context=None): + self.check(cr, uid, ids, 'unlink', context=context) + return super(mail_message, self).unlink(cr, uid, ids, context) + + def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, type='email', + email_cc=None, email_bcc=None, reply_to=False, partner_ids=None, attachments=None, + message_id=False, references=False, res_id=False, content_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 ``content_subtype`` + (by default, plaintext). If html content_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 partner_ids: destination partner_ids + :param string content_subtype: optional mime content_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 = {} + if partner_ids is None: + partner_ids = [] 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'), + 'date': fields.datetime.now(), 'user_id': uid, 'model': model, 'res_id': res_id, - 'type': 'email', - 'body_text': body if subtype != 'html' else False, - 'body_html': body if subtype == 'html' else False, + 'type': type, + 'body_text': body if content_subtype != 'html' else False, + 'body_html': body if content_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 '', + 'partner_ids': partner_ids, 'reply_to': reply_to, 'message_id': message_id, 'references': references, - 'subtype': subtype, + 'content_subtype': content_subtype, 'headers': headers, # serialize the dict on the fly 'mail_server_id': mail_server_id, 'state': 'outgoing', @@ -338,7 +387,10 @@ class mail_message(osv.osv): return email_msg_id def mark_outgoing(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'state':'outgoing'}, context) + return self.write(cr, uid, ids, {'state':'outgoing'}, context=context) + + def cancel(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state':'cancel'}, context=context) def process_email_queue(self, cr, uid, ids=None, context=None): """Send immediately queued messages, committing after each @@ -357,7 +409,7 @@ class mail_message(osv.osv): if context is None: context = {} if not ids: - filters = [('state', '=', 'outgoing')] + filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')] if 'filters' in context: filters.extend(context['filters']) ids = self.search(cr, uid, filters, context=context) @@ -371,7 +423,7 @@ class mail_message(osv.osv): _logger.exception("Failed processing mail queue") return res - def parse_message(self, message, save_original=False): + def parse_message(self, message, save_original=False, context=None): """Parses a string or email.message.Message representing an RFC-2822 email, and returns a generic dict holding the message details. @@ -394,7 +446,7 @@ class mail_message(osv.osv): 'headers' : { 'X-Mailer': mailer, #.. all X- headers... }, - 'subtype': msg_mime_subtype, + 'content_subtype': msg_mime_subtype, 'body_text': plaintext_body 'body_html': html_body, 'attachments': [('file1', 'bytes'), @@ -428,49 +480,52 @@ class mail_message(osv.osv): 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_fields = msg_txt.keys() msg['id'] = message_id msg['message-id'] = message_id - if 'Subject' in fields: + if 'Subject' in msg_fields: msg['subject'] = decode(msg_txt.get('Subject')) - if 'Content-Type' in fields: + if 'Content-Type' in msg_fields: msg['content-type'] = msg_txt.get('Content-Type') - if 'From' in fields: + if 'From' in msg_fields: msg['from'] = decode(msg_txt.get('From') or msg_txt.get_unixfrom()) - if 'To' in fields: + if 'To' in msg_fields: msg['to'] = decode(msg_txt.get('To')) - if 'Delivered-To' in fields: + if 'Delivered-To' in msg_fields: msg['to'] = decode(msg_txt.get('Delivered-To')) - if 'CC' in fields: + if 'CC' in msg_fields: msg['cc'] = decode(msg_txt.get('CC')) - if 'Cc' in fields: + if 'Cc' in msg_fields: msg['cc'] = decode(msg_txt.get('Cc')) - if 'Reply-To' in fields: + if 'Reply-To' in msg_fields: msg['reply'] = decode(msg_txt.get('Reply-To')) - if 'Date' in fields: + if 'Date' in msg_fields: date_hdr = decode(msg_txt.get('Date')) - msg['date'] = dateutil.parser.parse(date_hdr).strftime("%Y-%m-%d %H:%M:%S") + # convert from email timezone to server timezone + date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) + date_server_datetime_str = date_server_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + msg['date'] = date_server_datetime_str - if 'Content-Transfer-Encoding' in fields: + if 'Content-Transfer-Encoding' in msg_fields: msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') - if 'References' in fields: + if 'References' in msg_fields: msg['references'] = msg_txt.get('References') - if 'In-Reply-To' in fields: + if 'In-Reply-To' in msg_fields: msg['in-reply-to'] = msg_txt.get('In-Reply-To') msg['headers'] = {} - msg['subtype'] = 'plain' + msg['content_subtype'] = 'plain' for item in msg_txt.items(): if item[0].startswith('X-'): msg['headers'].update({item[0]: item[1]}) @@ -479,7 +534,7 @@ class mail_message(osv.osv): body = msg_txt.get_payload(decode=True) if 'text/html' in msg.get('content-type', ''): msg['body_html'] = body - msg['subtype'] = 'html' + msg['content_subtype'] = 'html' if body: body = tools.html2plaintext(body) msg['body_text'] = tools.ustr(body, encoding) @@ -488,9 +543,9 @@ class mail_message(osv.osv): 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' + msg['content_subtype'] = 'alternative' else: - msg['subtype'] = 'mixed' + msg['content_subtype'] = 'mixed' for part in msg_txt.walk(): if part.get_content_maintype() == 'multipart': continue @@ -504,7 +559,7 @@ class mail_message(osv.osv): content = tools.ustr(content, encoding) if part.get_content_subtype() == 'html': msg['body_html'] = content - msg['subtype'] = 'html' # html version prevails + msg['content_subtype'] = 'html' # html version prevails body = tools.ustr(tools.html2plaintext(content)) body = body.replace(' ', '') elif part.get_content_subtype() == 'plain': @@ -521,7 +576,7 @@ class mail_message(osv.osv): # for backwards compatibility: msg['body'] = msg['body_text'] - msg['sub_type'] = msg['subtype'] or 'plain' + msg['sub_type'] = msg['content_subtype'] or 'plain' return msg def _postprocess_sent_message(self, cr, uid, message, context=None): @@ -535,10 +590,9 @@ class mail_message(osv.osv): """ if message.auto_delete: self.pool.get('ir.attachment').unlink(cr, uid, - [x.id for x in message.attachment_ids \ - if x.res_model == self._name and \ - x.res_id == message.id], - context=context) + [x.id for x in message.attachment_ids + if x.res_model == self._name and x.res_id == message.id], + context=context) message.unlink() return True @@ -557,8 +611,6 @@ class mail_message(osv.osv): 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): @@ -567,37 +619,45 @@ class mail_message(osv.osv): for attach in message.attachment_ids: attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) - body = message.body_html if message.subtype == 'html' else message.body_text + body = message.body_html if message.content_subtype == 'html' else message.body_text body_alternative = None - subtype_alternative = None - if message.subtype == 'html' and message.body_text: + content_subtype_alternative = None + if message.content_subtype == 'html' and message.body_text: # we have a plain text alternative prepared, pass it to # build_message instead of letting it build one body_alternative = message.body_text - subtype_alternative = 'plain' + content_subtype_alternative = 'plain' + + # handle destination_partners + partner_ids_email_to = '' + for partner in message.partner_ids: + partner_ids_email_to += '%s ' % (partner.email or '') + message_email_to = '%s %s' % (partner_ids_email_to, message.email_to or '') + # build an RFC2822 email.message.Message object and send it + # without queuing msg = ir_mail_server.build_email( email_from=message.email_from, - email_to=to_email(message.email_to), + email_to=mail_tools_to_email(message_email_to), subject=message.subject, body=body, body_alternative=body_alternative, - email_cc=to_email(message.email_cc), - email_bcc=to_email(message.email_bcc), + email_cc=mail_tools_to_email(message.email_cc), + email_bcc=mail_tools_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, - subtype_alternative=subtype_alternative, + subtype=message.content_subtype, + subtype_alternative=content_subtype_alternative, headers=message.headers and ast.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}) + message.write({'state':'sent', 'message_id': res, 'email_to': message_email_to}) else: - message.write({'state':'exception'}) + message.write({'state':'exception', 'email_to': message_email_to}) message.refresh() if message.state == 'sent': self._postprocess_sent_message(cr, uid, message, context=context) @@ -609,8 +669,6 @@ class mail_message(osv.osv): 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 index 34ba6db1477..634b5317d27 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -30,17 +30,26 @@ <group> <group> <field name="subject"/> + <field name="user_id"/> <field name="date"/> <field name="type"/> - <field name="body_text"/> + <field name="content_subtype"/> </group> <group> - <field name="user_id" string="User"/> <field name="model"/> <field name="res_id"/> <field name="parent_id"/> + <field name="partner_ids" widget="many2many_tags"/> </group> </group> + <notebook> + <page string="Body (Rich)"> + <field name="body_html" widget="text_html"/> + </page> + <page string="Body (Plain)"> + <field name="body_text" widget="text"/> + </page> + </notebook> </sheet> </form> </field> @@ -73,7 +82,30 @@ <search string="Messages Search"> <field name="user_id"/> <field name="body"/> - <field name="model" string="Message" filter_domain="['|', ('subject', 'ilike', self), ('body_text', 'ilike', self)]" /> + <field name="subject"/> + <field name="type"/> + <filter icon="terp-personal+" string="My Feeds" + name="my_feeds" help="My Feeds" + domain="[('user_id','=',uid)]"/> + <filter icon="terp-personal+" string="Comments" + name="comments" help="Comments" + domain="[('type', '=', 'comment')]"/> + <filter icon="terp-personal+" string="Notifications" + name="notifications" help="Notifications" + domain="[('type', '=', 'notification')]"/> + <filter icon="terp-personal+" string="Emails" + name="emails" help="Emails" + domain="[('type', '=', 'email')]"/> + <filter icon="terp-go-today" string="Today" + name="today" help="Today" + domain="[ ('date', '<=', datetime.date.today().strftime('%%Y-%%m-%%d 23:59:59')), + ('date', '>=', datetime.date.today().strftime('%%Y-%%m-%%d 00:00:00')) + ]"/> + <filter icon="terp-go-week" string="This week" + name="7_days" help="This week" + domain="[ ('date', '<=', datetime.date.today().strftime('%%Y-%%m-%%d 23:59:59')), + ('date', '>=', (datetime.date.today()-datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d 00:00:00')) + ]"/> </search> </field> </record> @@ -86,55 +118,60 @@ <form string="Email message" version="7.0"> <sheet> <label for="subject" class="oe_edit_only"/> - <h1><field name="subject"/></h1> - <label for="user_id"/> - <h2><field name="user_id" class="oe_inline" string="User"/> on <field name="date" class="oe_inline"/></h2> - <group col="4"> - <field name="partner_id" readonly="1" attrs="{'invisible':[('partner_id', '=', False)]}"/> - <field name="type"/> - </group> + <h2><field name="subject"/></h2> + <div> + by <field name="user_id" class="oe_inline" string="User"/> on <field name="date" class="oe_inline"/> + <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'/> + </div> <notebook colspan="4"> - <page string="Details"> - <group string="Recipients"> - <field name="email_from"/> - <field name="email_to"/> - <field name="email_cc" attrs="{'invisible':[('email_cc', '=', False)]}"/> - <field name="email_bcc" attrs="{'invisible':[('email_bcc', '=', False)]}"/> - <field name="reply_to" attrs="{'invisible':[('reply_to', '=', False)]}"/> - </group> - <group col="4" string="Message Details"> - <field name="model"/> - <button name="open_document" string="Open" type="object" icon="gtk-jump-to" colspan="2"/> - <field name="res_id"/> - <field name="message_id" colspan="4" attrs="{'invisible':[('message_id', '=', False)]}"/> - <field name="references" colspan="4" widget="char" size="512" attrs="{'invisible':[('references', '=', False)]}"/> + <page string="Message Details"> + <group> + <group> + <field name="email_from"/> + <field name="email_to"/> + <field name="email_cc"/> + <field name="email_bcc"/> + <field name="reply_to"/> + </group> + <group> + <field name="partner_id" readonly="1"/> + <field name="partner_ids" widget="many2many_tags"/> + </group> </group> <notebook> - <page string="Body (Rich)" attrs="{'invisible':[('subtype','=','plain')]}"> + <page string="Body (Rich)"> <field name="body_html" widget="text_html"/> </page> <page string="Body (Plain)"> <field name="body_text" widget="text"/> </page> </notebook> - <group col="5"> - <field name="state" colspan="2"/> - <field name="subtype" attrs="{'invisible':[('subtype', '=', False)]}"/> - <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'/> + </page> + <page string="Advanced" groups="base.group_no_one"> + <group> + <group> + <field name="auto_delete"/> + <field name="type"/> + <field name="content_subtype"/> + <field name="state" colspan="2"/> + <field name="mail_server_id"/> + <field name="original"/> + <field name="model"/> + <field name="res_id"/> + <button name="open_document" string="Open" type="object" icon="gtk-jump-to" colspan="2" + attrs="{'invisible':['|', ('model', '=', ''), ('res_id', '=', False)]}"/> + </group> + <group> + <field name="message_id"/> + <field name="references"/> + <field name="headers"/> + </group> </group> </page> <page string="Attachments"> <field name="attachment_ids"/> </page> - <page string="Advanced" groups="base.group_no_one"> - <group col="2" colspan="4"> - <field name="mail_server_id" attrs="{'invisible':[('mail_server_id', '=', 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> </sheet> </form> @@ -236,13 +273,13 @@ <record id="action_mail_all_feeds" model="ir.actions.client"> <field name="name">News Feed</field> - <field name="tag">mail.all_feeds</field> + <field name="tag">mail.wall</field> <field name="params" eval="{'search_view_id': ref('view_message_search_wall')}"/> </record> <record id="action_mail_my_feeds" model="ir.actions.client"> <field name="name">My Feeds</field> - <field name="tag">mail.all_feeds</field> + <field name="tag">mail.wall</field> <field name="params" eval="{'search_view_id': ref('view_message_search_wall'), 'my_feeds': True}"/> </record> </data> diff --git a/addons/mail/mail_subscription_view.xml b/addons/mail/mail_subscription_view.xml index 7dbaea098dc..c3d79d48308 100644 --- a/addons/mail/mail_subscription_view.xml +++ b/addons/mail/mail_subscription_view.xml @@ -52,11 +52,13 @@ <field name="view_mode">tree,form</field> </record> - <!-- Add menu entry in Settings/Email --> - <menuitem name="Subscriptions" id="menu_email_subscriptions" parent="base.menu_email" action="action_view_subscriptions" sequence="30" groups="base.group_no_one"/> + <!-- Add subscriptions related menu entries in Settings/Email --> + <menuitem name="Subscriptions" id="menu_email_subscriptions" parent="base.menu_email" + action="action_view_subscriptions" sequence="30" groups="base.group_no_one"/> --> - <!-- Add menu entry in Settings/Email --> - <menuitem name="Notifications" id="menu_email_notifications" parent="base.menu_email" action="action_view_notifications" sequence="31" groups="base.group_no_one"/> + <!-- Add notifications related menu entry in Settings/Email --> + <menuitem name="Notifications" id="menu_email_notifications" parent="base.menu_email" + action="action_view_notifications" sequence="35" groups="base.group_no_one"/> </data> </openerp> diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index cdcaf4f464d..d036150b21c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -65,7 +65,7 @@ class mail_thread(osv.Model): def _get_message_ids(self, cr, uid, ids, name, args, context=None): res = {} for id in ids: - message_ids = self.message_load_ids(cr, uid, [id], context=context) + message_ids = self.message_search(cr, uid, [id], context=context) subscriber_ids = self.message_get_subscribers(cr, uid, [id], context=context) res[id] = { 'message_ids': message_ids, @@ -130,7 +130,7 @@ class mail_thread(osv.Model): # delete subscriptions subscr_to_del_ids = subscr_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) subscr_obj.unlink(cr, uid, subscr_to_del_ids, context=context) - # delete notifications + # delete messages and notifications msg_to_del_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context) msg_obj.unlink(cr, uid, msg_to_del_ids, context=context) @@ -150,39 +150,13 @@ class mail_thread(osv.Model): context = {} message_obj = self.pool.get('mail.message') - subscription_obj = self.pool.get('mail.subscription') notification_obj = self.pool.get('mail.notification') - res_users_obj = self.pool.get('res.users') - body = vals.get('body_html', '') if vals.get('subtype', 'plain') == 'html' else vals.get('body_text', '') - + body = vals.get('body_html', '') if vals.get('content_subtype') == 'html' else vals.get('body_text', '') + # automatically subscribe the writer of the message if vals['user_id']: self.message_subscribe(cr, uid, [thread_id], [vals['user_id']], context=context) - - # get users that will get a notification pushed - user_to_push_ids = self.message_create_get_notification_user_ids(cr, uid, [thread_id], vals, context=context) - user_to_push_from_parse_ids = self.message_parse_users(cr, uid, [thread_id], body, context=context) - - # set email_from and email_to for comments and notifications - if vals.get('type', False) and vals['type'] == 'comment' or vals['type'] == 'notification': - current_user = res_users_obj.browse(cr, uid, [uid], context=context)[0] - if not vals.get('email_from', False): - vals['email_from'] = current_user.user_email - if not vals.get('email_to', False): - email_to = '' - for user in res_users_obj.browse(cr, uid, user_to_push_ids, context=context): - if not user.notification_email_pref == 'all' and \ - not (user.notification_email_pref == 'comments' and vals['type'] == 'comment') and \ - not (user.notification_email_pref == 'to_me' and user.id in user_to_push_from_parse_ids): - continue - if not user.user_email: - continue - email_to = '%s, %s' % (email_to, user.user_email) - email_to = email_to.lstrip(', ') - if email_to: - vals['email_to'] = email_to - vals['state'] = 'outgoing' - + # create message msg_id = message_obj.create(cr, uid, vals, context=context) @@ -191,43 +165,50 @@ class mail_thread(osv.Model): # special: if install mode, do not push demo data if context.get('install_mode', False): - return True - - # push to users + return msg_id + + # get users that will get a notification pushed + user_to_push_ids = self.message_get_user_ids_to_notify(cr, uid, [thread_id], vals, context=context) for id in user_to_push_ids: notification_obj.create(cr, uid, {'user_id': id, 'message_id': msg_id}, context=context) - + + # create the email to send + email_id = self.message_create_notify_by_email(cr, uid, vals, user_to_push_ids, context=context) + return msg_id - - def message_create_get_notification_user_ids(self, cr, uid, thread_ids, new_msg_vals, context=None): - if context is None: - context = {} - - notif_user_ids = [] - body = new_msg_vals.get('body_html', '') if new_msg_vals.get('subtype', 'plain') == 'html' else new_msg_vals.get('body_text', '') - for thread_id in thread_ids: - # add subscribers - notif_user_ids += self.message_get_subscribers(cr, uid, [thread_id], context=context) - # add users requested via parsing message (@login) - notif_user_ids += self.message_parse_users(cr, uid, [thread_id], body, context=context) - # add users requested to perform an action (need_action mechanism) - if hasattr(self, 'get_needaction_user_ids'): - notif_user_ids += self.get_needaction_user_ids(cr, uid, [thread_id], context=context)[thread_id] - # add users notified of the parent messages (because: if parent message contains @login, login must receive the replies) - if new_msg_vals.get('parent_id'): - notif_obj = self.pool.get('mail.notification') - parent_notif_ids = notif_obj.search(cr, uid, [('message_id', '=', new_msg_vals.get('parent_id'))], context=context) - parent_notifs = notif_obj.read(cr, uid, parent_notif_ids, context=context) - notif_user_ids += [parent_notif['user_id'][0] for parent_notif in parent_notifs] + + def message_get_user_ids_to_notify(self, cr, uid, thread_ids, new_msg_vals, context=None): + subscription_obj = self.pool.get('mail.subscription') + # get body + body = new_msg_vals.get('body_html', '') if new_msg_vals.get('content_subtype') == 'html' else new_msg_vals.get('body_text', '') + + # get subscribers + notif_user_ids = self.message_get_subscribers(cr, uid, thread_ids, context=context) + + # add users requested via parsing message (@login) + notif_user_ids += self.message_parse_users(cr, uid, body, context=context) + + # add users requested to perform an action (need_action mechanism) + if hasattr(self, 'get_needaction_user_ids'): + user_ids_dict = self.get_needaction_user_ids(cr, uid, thread_ids, context=context) + for id, user_ids in user_ids_dict.iteritems(): + notif_user_ids += user_ids + + # add users notified of the parent messages (because: if parent message contains @login, login must receive the replies) + if new_msg_vals.get('parent_id'): + notif_obj = self.pool.get('mail.notification') + parent_notif_ids = notif_obj.search(cr, uid, [('message_id', '=', new_msg_vals.get('parent_id'))], context=context) + parent_notifs = notif_obj.read(cr, uid, parent_notif_ids, context=context) + notif_user_ids += [parent_notif['user_id'][0] for parent_notif in parent_notifs] # remove duplicate entries notif_user_ids = list(set(notif_user_ids)) return notif_user_ids - def message_parse_users(self, cr, uid, ids, string, context=None): + def message_parse_users(self, cr, uid, string, context=None): """Parse message content - if find @login -(^|\s)@((\w|@|\.)*)-: returns the related ids - this supports login that are emails (such as @admin@lapin.net) + this supports login that are emails (such as @raoul@grobedon.net) """ regex = re.compile('(^|\s)@((\w|@|\.)*)') login_lst = [item[1] for item in regex.findall(string)] @@ -248,46 +229,61 @@ class mail_thread(osv.Model): return ret_dict def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None, - parent_id=False, type='email', subtype='plain', state='received', - 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, 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 ``email_from`` is not set or ``type`` not set as 'email', - a note message is created, without the usual envelope - attributes (sender, recipients, etc.). - The creation of the message is done by calling ``message_create`` - method, that will manage automatic pushing of notifications. - - :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 body_text: plaintext contents of the mail or log message - :param body_html: html contents of the mail or log message - :param parent_id: id of the parent message (threaded messaging model) - :param type: optional type of message: 'email', 'comment', 'notification' - :param subtype: optional subtype of message: 'plain' or 'html', corresponding to the main - body contents (body_text or body_html). - :param state: optional state of message; 'received' by default - :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 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). + type='email', email_date=None, parent_id=False, + content_subtype='plain', state=None, + partner_ids=None, email_from=False, email_to=False, + email_cc=None, email_bcc=None, reply_to=None, + headers=None, message_id=False, references=None, + attachments=None, original=None, context=None): + """ Creates a new mail.message through message_create. The new message + is 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. + + This method calls message_create that will handle management of + subscription and notifications, and effectively create the message. + + If ``email_from`` is not set or ``type`` not set as 'email', + a note message is created (comment or system notification), + 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; + this is totally optional as subjects are not important except + for specific messages (blog post, job offers) or for emails + :param body_text: plaintext contents of the mail or log message + :param body_html: html contents of the mail or log message + :param type: type of message: 'email', 'comment', 'notification'; + email by default + :param email_date: email date string if different from now, in + server timezone + :param parent_id: id of the parent message (threaded messaging model) + :param content_subtype: optional content_subtype of message: 'plain' + or 'html', corresponding to the main body contents (body_text or + body_html). + :param state: state of message + :param partner_ids: destination partners of the message, in addition + to the now fully optional email_to; this method is supposed to + received a list of ids is not None. The specific many2many + instruction will be generated by this method. + :param email_from: Email From / Sender address if any + :param email_to: Email-To / Recipient address + :param email_cc: Comma-Separated list of Carbon Copy Emails To + addresses if any + :param email_bcc: Comma-Separated list of Blind Carbon Copy Emails To + addresses if any + :param reply_to: reply_to header + :param headers: mail headers to store + :param message_id: optional email identifier + :param references: optional email references + :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 = {} @@ -323,10 +319,15 @@ class mail_thread(osv.Model): 'res_id': thread.id, } to_attach.append(ir_attachment.create(cr, uid, data_attach, context=context)) - + # find related partner: partner_id column in thread object, or self is res.partner model partner_id = ('partner_id' in thread._columns.keys()) 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 + # destination partners + if partner_ids is None: + partner_ids = [] + mail_partner_ids = [(6, 0, partner_ids)] + data = { 'subject': subject, 'body_text': body_text or (hasattr(thread, 'description') and thread.description or ''), @@ -334,9 +335,10 @@ class mail_thread(osv.Model): 'parent_id': parent_id, 'date': email_date or fields.datetime.now(), 'type': type, - 'subtype': subtype, + 'content_subtype': content_subtype, 'state': state, 'message_id': message_id, + 'partner_ids': mail_partner_ids, 'attachment_ids': [(6, 0, to_attach)], 'user_id': uid, 'model' : thread._name, @@ -349,7 +351,6 @@ class mail_thread(osv.Model): if isinstance(param, list): param = ", ".join(param) data.update({ - 'subject': subject or _('History'), 'email_to': email_to, 'email_from': email_from or \ (hasattr(thread, 'user_id') and thread.user_id and thread.user_id.user_email), @@ -385,8 +386,9 @@ class mail_thread(osv.Model): body_html= msg_dict.get('body_html'), parent_id = msg_dict.get('parent_id', False), type = msg_dict.get('type', 'email'), - subtype = msg_dict.get('subtype', 'plain'), - state = msg_dict.get('state', 'received'), + content_subtype = msg_dict.get('content_subtype'), + state = msg_dict.get('state'), + partner_ids = msg_dict.get('partner_ids'), email_from = msg_dict.get('from', msg_dict.get('email_from')), email_to = msg_dict.get('to', msg_dict.get('email_to')), email_cc = msg_dict.get('cc', msg_dict.get('email_cc')), @@ -401,59 +403,109 @@ class mail_thread(osv.Model): original = msg_dict.get('original'), context = context) - def _message_add_ancestor_ids(self, cr, uid, ids, child_ids, root_ids, context=None): - """ Given message child_ids - Find their ancestors until root ids""" - if context is None: - context = {} - msg_obj = self.pool.get('mail.message') - tmp_msgs = msg_obj.read(cr, uid, child_ids, ['id', 'parent_id'], context=context) - parent_ids = [msg['parent_id'][0] for msg in tmp_msgs if msg['parent_id'] and msg['parent_id'][0] not in root_ids and msg['parent_id'][0] not in child_ids] + #------------------------------------------------------ + # Message loading + #------------------------------------------------------ + + def _message_search_ancestor_ids(self, cr, uid, ids, child_ids, ancestor_ids, context=None): + """ Given message child_ids ids, find their ancestors until ancestor_ids + using their parent_id relationship. + + :param child_ids: the first nodes of the search + :param ancestor_ids: list of ancestors. When the search reach an + ancestor, it stops. + """ + def _get_parent_ids(message_list, ancestor_ids, child_ids): + """ Tool function: return the list of parent_ids of messages + contained in message_list. Parents that are in ancestor_ids + or in child_ids are not returned. """ + return [message['parent_id'][0] for message in message_list + if message['parent_id'] + and message['parent_id'][0] not in ancestor_ids + and message['parent_id'][0] not in child_ids + ] + + message_obj = self.pool.get('mail.message') + messages_temp = message_obj.read(cr, uid, child_ids, ['id', 'parent_id'], context=context) + parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids) child_ids += parent_ids cur_iter = 0; max_iter = 100; # avoid infinite loop while (parent_ids and (cur_iter < max_iter)): cur_iter += 1 - tmp_msgs = msg_obj.read(cr, uid, parent_ids, ['id', 'parent_id'], context=context) - parent_ids = [msg['parent_id'][0] for msg in tmp_msgs if msg['parent_id'] and msg['parent_id'][0] not in root_ids and msg['parent_id'][0] not in child_ids] + messages_temp = message_obj.read(cr, uid, parent_ids, ['id', 'parent_id'], context=context) + parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids) child_ids += parent_ids if (cur_iter > max_iter): - _logger.warning("Possible infinite loop in _message_add_ancestor_ids. Note that this algorithm is intended to check for cycle in message graph.") + _logger.warning("Possible infinite loop in _message_search_ancestor_ids. "\ + "Note that this algorithm is intended to check for cycle in "\ + "message graph, leading to a curious error. Have fun.") return child_ids - def message_load_ids(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[], context=None): - """ OpenChatter feature: return thread messages ids. It searches in - mail.messages where res_id = ids, (res_)model = current model. - :param domain: domain to add to the search; especially child_of - is interesting when dealing with threaded display - :param ascent: performs an ascended search; will add to fetched msgs - all their parents until root_ids - :param root_ids: for ascent search - :param root_ids: root_ids when performing an ascended search + def message_search_get_domain(self, cr, uid, ids, context=None): + """ OpenChatter feature: get the domain to search the messages related + to a document. mail.thread defines the default behavior as + being messages with model = self._name, id in ids. + This method should be overridden if a model has to implement a + particular behavior. """ - if context is None: - context = {} - msg_obj = self.pool.get('mail.message') - msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', 'in', ids), ('model', '=', self._name)] + domain, - limit=limit, offset=offset, context=context) - if (ascent): msg_ids = self._message_add_ancestor_ids(cr, uid, ids, msg_ids, root_ids, context=context) - return msg_ids + return ['&', ('res_id', 'in', ids), ('model', '=', self._name)] - def message_load(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[], context=None): - """ OpenChatter feature: return thread messages + def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, + limit=100, offset=0, domain=None, count=False, context=None): + """ OpenChatter feature: return thread messages ids according to the + search domain given by ``message_search_get_domain``. + + It is possible to add in the search the parent of messages by + setting the fetch_ancestors flag to True. In that case, using + the parent_id relationship, the method returns the id list according + to the search domain, but then calls ``_message_search_ancestor_ids`` + that will add to the list the ancestors ids. The search is limited + to parent messages having an id in ancestor_ids or having + parent_id set to False. + + If ``count==True``, the number of ids is returned instead of the + id list. The count is done by hand instead of passing it as an + argument to the search call because we might want to perform + a research including parent messages until some ancestor_ids. + + :param fetch_ancestors: performs an ascended search; will add + to fetched msgs all their parents until + ancestor_ids + :param ancestor_ids: used when fetching ancestors + :param domain: domain to add to the search; especially child_of + is interesting when dealing with threaded display. + Note that the added domain is anded with the + default domain. + :param limit, offset, count, context: as usual """ - msg_ids = self.message_load_ids(cr, uid, ids, limit, offset, domain, ascent, root_ids, context=context) - msgs = self.pool.get('mail.message').read(cr, uid, msg_ids, [], context=context) - - # Set as read - self.message_check_and_set_read(cr, uid, ids, context=context) - + search_domain = self.message_search_get_domain(cr, uid, ids, context=context) + if domain: + search_domain += domain + message_obj = self.pool.get('mail.message') + message_res = message_obj.search(cr, uid, search_domain, limit=limit, offset=offset, count=count, context=context) + if not count and fetch_ancestors: + message_res += self._message_search_ancestor_ids(cr, uid, ids, message_res, ancestor_ids, context=context) + return message_res + + def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, + limit=100, offset=0, domain=None, context=None): + """ OpenChatter feature: read the messages related to some threads. + This method is used mainly the Chatter widget, to directly have + read result instead of searching then reading. + + Please see message_search for more information about the parameters. + """ + message_ids = self.message_search(cr, uid, ids, fetch_ancestors, ancestor_ids, + limit, offset, domain, context=context) + messages = self.pool.get('mail.message').read(cr, uid, message_ids, context=context) + """ Retrieve all attachments names """ + map_id_to_name = dict((attachment_id, '') for message in messages for attachment_id in message['attachment_ids']) map_id_to_name = {} - - for msg in msgs: + for msg in messages: for attach_id in msg["attachment_ids"]: map_id_to_name[attach_id] = '' # use empty string as a placeholder - + ids = map_id_to_name.keys() names = self.pool.get('ir.attachment').name_get(cr, uid, ids, context=context) @@ -462,26 +514,34 @@ class mail_thread(osv.Model): map_id_to_name[name[0]] = name[1] # give corresponding ids and names to each message - for msg in msgs: + for msg in messages: msg["attachments"] = [] for attach_id in msg["attachment_ids"]: msg["attachments"].append({'id': attach_id, 'name': map_id_to_name[attach_id]}) - """ Sort and return messages """ - msgs = sorted(msgs, key=lambda d: (-d['id'])) - return msgs - - def get_pushed_messages(self, cr, uid, ids, limit=100, offset=0, msg_search_domain=[], ascent=False, root_ids=[], context=None): - """ OpenChatter: wall: get messages to display (=pushed notifications) + # Set the threads as read + self.message_check_and_set_read(cr, uid, ids, context=context) + # Sort and return the messages + messages = sorted(messages, key=lambda d: (-d['id'])) + return messages + + def message_get_pushed_messages(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, + limit=100, offset=0, msg_search_domain=[], context=None): + """ OpenChatter: wall: get the pushed notifications and used them + to fetch messages to display on the wall. + + :param fetch_ancestors: performs an ascended search; will add + to fetched msgs all their parents until + ancestor_ids + :param ancestor_ids: used when fetching ancestors :param domain: domain to add to the search; especially child_of is interesting when dealing with threaded display :param ascent: performs an ascended search; will add to fetched msgs all their parents until root_ids :param root_ids: for ascent search - :return list of mail.messages sorted by date + :return: list of mail.messages sorted by date """ - if context is None: context = {} notification_obj = self.pool.get('mail.notification') msg_obj = self.pool.get('mail.message') # update message search @@ -496,7 +556,7 @@ class mail_thread(osv.Model): msg_ids = [notification.message_id.id for notification in notifications] # get messages msg_ids = msg_obj.search(cr, uid, [('id', 'in', msg_ids)], context=context) - if (ascent): msg_ids = self._message_add_ancestor_ids(cr, uid, ids, msg_ids, root_ids, context=context) + if (fetch_ancestors): msg_ids = self._message_search_ancestor_ids(cr, uid, ids, msg_ids, ancestor_ids, context=context) msgs = msg_obj.read(cr, uid, msg_ids, context=context) return msgs @@ -524,9 +584,9 @@ class mail_thread(osv.Model): 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. + 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. + 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 @@ -540,7 +600,6 @@ class mail_thread(osv.Model): 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, @@ -548,8 +607,11 @@ class mail_thread(osv.Model): 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) + msg = mail_message.parse_message(msg_txt, save_original=save_original, context=context) + # update state + msg['state'] = 'received' + if strip_attachments and 'attachments' in msg: del msg['attachments'] @@ -587,6 +649,8 @@ class mail_thread(osv.Model): res_id = create_record(msg) # To forward the email to other followers self.message_forward(cr, uid, model, [res_id], msg_txt, context=context) + # Set as Unread + model_pool.message_mark_as_unread(cr, uid, [res_id], context=context) return res_id def message_new(self, cr, uid, msg_dict, custom_values=None, context=None): @@ -652,9 +716,9 @@ class mail_thread(osv.Model): return self.message_append_dict(cr, uid, ids, msg_dict, context=context) 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. + """ 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)): @@ -748,45 +812,27 @@ class mail_thread(osv.Model): # Note specific #------------------------------------------------------ - def message_broadcast(self, cr, uid, ids, subject=None, body=None, parent_id=False, type='notification', subtype='html', context=None): - if context is None: - context = {} - notification_obj = self.pool.get('mail.notification') - # write message - msg_ids = self.message_append_note(cr, uid, ids, subject=subject, body=body, parent_id=parent_id, type=type, subtype=subtype, context=context) - # escape if in install mode or note writing was not successfull - if 'install_mode' in context: - return True - if not isinstance(msg_ids, (list)): - return True - # get already existing notigications - notification_ids = notification_obj.search(cr, uid, [('message_id', 'in', msg_ids)], context=context) - already_pushed_user_ids = map(itemgetter('user_id'), notification_obj.read(cr, uid, notification_ids, context=context)) - # get base.group_user group - res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') or False - group_id = res and res[1] or False - if not group_id: return True - group = self.pool.get('res.groups').browse(cr, uid, [group_id], context=context)[0] - for user in group.users: - if user.id in already_pushed_user_ids: continue - for msg_id in msg_ids: - notification_obj.create(cr, uid, {'user_id': user.id, 'message_id': msg_id}, context=context) - return True - def log(self, cr, uid, id, message, secondary=False, context=None): - _logger.warning("log() is deprecated. Please use OpenChatter notification system instead of the res.log mechanism.") + _logger.warning("log() is deprecated. As this module inherit from \ + mail.thread, the message will be managed by this \ + module instead of by the res.log mechanism. Please \ + use the mail.thread OpenChatter API instead of the \ + now deprecated res.log.") self.message_append_note(cr, uid, [id], 'res.log', message, context=context) - def message_append_note(self, cr, uid, ids, subject=None, body=None, parent_id=False, type='notification', subtype='html', context=None): - if type in ['notification', 'reply']: + def message_append_note(self, cr, uid, ids, subject=None, body=None, parent_id=False, + type='notification', content_subtype='html', context=None): + if type in ['notification', 'comment']: subject = None - if subtype == 'html': + if content_subtype == 'html': body_html = body body_text = body else: body_html = body body_text = body - return self.message_append(cr, uid, ids, subject, body_html=body_html, body_text=body_text, parent_id=parent_id, type=type, subtype=subtype, context=context) + return self.message_append(cr, uid, ids, subject, body_html, body_text, + type, parent_id=parent_id, + content_subtype=content_subtype, context=context) #------------------------------------------------------ # Subscription mechanism @@ -796,9 +842,9 @@ class mail_thread(osv.Model): """ Returns the current document followers. Basically this method checks in mail.subscription for entries with matching res_model, res_id. - - :param get_ids: if set to True, return the ids of users; if set - to False, returns the result of a read in res.users + This method can be overriden to add implicit subscribers, such + as project managers, by adding their user_id to the list of + ids returned by this method. """ subscr_obj = self.pool.get('mail.subscription') subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) @@ -849,19 +895,97 @@ class mail_thread(osv.Model): # Trying to unsubscribe somebody not in subscribers: returns False # if special management is needed; allows to know that an automatically # subscribed user tries to unsubscribe and allows to warn him - mail_thread_model = self.pool.get('mail.thread') - if not user_ids and not uid in mail_thread_model.message_get_subscribers(cr, uid, ids, context=context): - return False - subscription_obj = self.pool.get('mail.subscription') to_unsubscribe_uids = [uid] if user_ids is None else user_ids + subscription_obj = self.pool.get('mail.subscription') to_delete_sub_ids = subscription_obj.search(cr, uid, ['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('user_id', 'in', to_unsubscribe_uids)], context=context) + if not to_delete_sub_ids: + return False return subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context) #------------------------------------------------------ # Notification API #------------------------------------------------------ + def message_create_notify_by_email(self, cr, uid, new_msg_values, user_to_notify_ids, context=None): + """ When creating a new message and pushing notifications, emails + must be send if users have chosen to receive notifications + by email via the notification_email_pref field. + + ``notification_email_pref`` can have 3 values : + - all: receive all notification by email (for example for shared + users) + - to_me: messages send directly to me (@login, messages on res.users) + - never: never receive notifications + Note that an user should never receive notifications for messages + he has created. + + :param new_msg_values: dictionary of message values, those that + are given to the create method + :param user_to_notify_ids: list of user_ids, user that will + receive a notification on their Wall + """ + message_obj = self.pool.get('mail.message') + res_users_obj = self.pool.get('res.users') + body = new_msg_values.get('body_html', '') if new_msg_values.get('content_subtype') == 'html' else new_msg_values.get('body_text', '') + + # remove message writer + if user_to_notify_ids.count(new_msg_values.get('user_id')) > 0: + user_to_notify_ids.remove(new_msg_values.get('user_id')) + + # get user_ids directly asked + user_to_push_from_parse_ids = self.message_parse_users(cr, uid, body, context=context) + + # try to find an email_to + email_to = '' + for user in res_users_obj.browse(cr, uid, user_to_notify_ids, context=context): + if not user.notification_email_pref == 'all' and \ + not (user.notification_email_pref == 'to_me' and user.id in user_to_push_from_parse_ids): + continue + if not user.user_email: + continue + email_to = '%s, %s' % (email_to, user.user_email) + email_to = email_to.lstrip(', ') + + # did not find any email address: not necessary to create an email + if not email_to: + return + + # try to find an email_from + current_user = res_users_obj.browse(cr, uid, [uid], context=context)[0] + email_from = new_msg_values.get('email_from') + if not email_from: + email_from = current_user.user_email + + # get email content, create it (with mail_message.create) + email_values = self.message_create_notify_get_email_dict(cr, uid, new_msg_values, email_from, email_to, context) + email_id = message_obj.create(cr, uid, email_values, context=context) + return email_id + + def message_create_notify_get_email_dict(self, cr, uid, new_msg_values, email_from, email_to, context=None): + values = dict(new_msg_values) + + body_html = new_msg_values.get('body_html', '') + if body_html: + body_html += '\n\n----------\nThis email was send automatically by OpenERP, because you have subscribed to a document.' + body_text = new_msg_values.get('body_text', '') + if body_text: + body_text += '\n\n----------\nThis email was send automatically by OpenERP, because you have subscribed to a document.' + values.update({ + 'type': 'email', + 'state': 'outgoing', + 'email_from': email_from, + 'email_to': email_to, + 'subject': 'New message', + 'content_subtype': new_msg_values.get('content_subtype', 'plain'), + 'body_html': body_html, + 'body_text': body_text, + 'auto_delete': True, + 'res_model': '', + 'res_id': False, + }) + return values + def message_remove_pushed_notifications(self, cr, uid, ids, msg_ids, remove_childs=True, context=None): notif_obj = self.pool.get('mail.notification') msg_obj = self.pool.get('mail.message') diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py index 922f3e4b6ed..3dc1f91d857 100644 --- a/addons/mail/res_partner.py +++ b/addons/mail/res_partner.py @@ -22,34 +22,18 @@ from osv import osv from osv import fields -class res_partner(osv.osv): +class res_partner_mail(osv.osv): """ Inherits partner and adds CRM information in the partner form """ _name = "res.partner" _inherit = ['res.partner', 'mail.thread'] - _columns = { - 'emails': fields.one2many('mail.message', 'partner_id', 'Emails', readonly=True, domain=[('email_from','!=',False)]), - } - def message_load_ids(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[False], context=None): - """ Override of message_load_ids - partner discussion page : - - messages posted on res.partner, partner_id = partner.id - - messages directly sent to partner + def message_search_get_domain(self, cr, uid, ids, context=None): + """ Override of message_search_get_domain for partner discussion page. + The purpose is to add messages directly sent to the partner. """ - msg_obj = self.pool.get('mail.message') - msg_ids = [] - partner_ids=[] - for partner in self.browse(cr, uid, ids, context=context): - msg_ids += msg_obj.search(cr, uid, [ ('res_id', '=', partner.id), ('model', '=' ,self._name)] + domain, - limit=limit, offset=offset, context=context) - if self._name=='res.partner': - partner_ids=msg_obj.search(cr, uid, [ ('partner_id', 'in', ids)] + domain, - limit=limit, offset=offset, context=context) - if partner_ids : - msg_ids+= partner_ids - if (ascent): msg_ids = self._message_add_ancestor_ids(cr, uid, ids, msg_ids, root_ids, context=context) - return msg_ids - -res_partner() + initial_domain = super(res_partner_mail, self).message_search_get_domain(cr, uid, ids, context=context) + if self._name == 'res.partner': # to avoid models inheriting from res.partner + search_domain = ['|'] + initial_domain + ['|', ('partner_id', 'in', ids), ('partner_ids', 'in', ids)] + return search_domain # 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 index 2d425f18c7b..d1e2d881739 100644 --- a/addons/mail/res_partner_view.xml +++ b/addons/mail/res_partner_view.xml @@ -9,8 +9,8 @@ <field name="inherit_id" ref="base.view_partner_form"/> <field name="arch" type="xml"> <xpath expr="//sheet" position="after"> - <div class="oe_chatter"> - <field name="message_ids" widget="mail_thread" + <div class="oe_chatter oe_mail_group_footer"> + <field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/> </div> </xpath> diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index f62e29fb44d..89767473754 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -24,24 +24,26 @@ from tools.translate import _ class res_users(osv.osv): """ Update of res.users class - - add a preference about sending emails about notificatoins + - add a preference about sending emails about notifications - make a new user follow itself + - add a welcome message """ _name = 'res.users' _inherit = ['res.users', 'mail.thread'] _columns = { 'notification_email_pref': fields.selection([ - ('all', 'All feeds'), - ('comments', 'Only comments'), - ('to_me', 'Only when sent directly to me'), - ('none', 'Never') - ], 'Receive Feeds by Email', required=True, - help="Choose in which case you want to receive an email when you receive new feeds."), + ('all', 'All feeds'), + ('comments', 'Only comments'), + ('to_me', 'Only when sent directly to me'), + ('none', 'Never') + ], 'Receive Feeds by Email', required=True, + help="Choose in which case you want to receive an email when you "\ + "receive new feeds."), } _defaults = { - 'notification_email_pref': 'none', + 'notification_email_pref': 'to_me', } def __init__(self, pool, cr): @@ -60,25 +62,61 @@ class res_users(osv.osv): user = self.browse(cr, uid, [user_id], context=context)[0] # make user follow itself self.message_subscribe(cr, uid, [user_id], [user_id], context=context) - # create a welcome message to broadcast + # create a welcome message company_name = user.company_id.name if user.company_id else 'the company' - message = _('%s has joined %s! You may leave him/her a message to celebrate a new arrival in the company ! You can help him/her doing its first steps on OpenERP.') % (user.name, company_name) - # TODO: clean the broadcast feature. As this is not cleany specified, temporarily remove the message broadcasting that is not buggy but not very nice. - #self.message_broadcast(cr, uid, [user.id], 'Welcome notification', message, context=context) + message = _('%s has joined %s! Welcome in OpenERP !') % (user.name, company_name) + self.message_append_note(cr, uid, [user_id], subject='Welcom to OpenERP', body=message, type='comment', context=context) return user_id - def message_load_ids(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[False], context=None): - """ Override of message_load_ids - User discussion page : - - messages posted on res.users, res_id = user.id - - messages directly sent to user with @user_login + def message_search_get_domain(self, cr, uid, ids, context=None): + """ Override of message_search_get_domain for partner discussion page. + The purpose is to add messages directly sent to user using + @user_login. """ - if context is None: - context = {} - msg_obj = self.pool.get('mail.message') - msg_ids = [] + initial_domain = super(res_users, self).message_search_get_domain(cr, uid, ids, context=context) + custom_domain = [] for user in self.browse(cr, uid, ids, context=context): - msg_ids += msg_obj.search(cr, uid, ['|', '|', ('body_text', 'like', '@%s' % (user.login)), ('body_html', 'like', '@%s' % (user.login)), '&', ('res_id', '=', user.id), ('model', '=', self._name)] + domain, - limit=limit, offset=offset, context=context) - if (ascent): msg_ids = self._message_add_ancestor_ids(cr, uid, ids, msg_ids, root_ids, context=context) - return msg_ids + if custom_domain: + custom_domain += ['|'] + custom_domain += ['|', ('body_text', 'like', '@%s' % (user.login)), ('body_html', 'like', '@%s' % (user.login))] + return ['|'] + initial_domain + custom_domain + +class res_users_mail_group(osv.osv): + """ Update of res.groups class + - if adding/removing users from a group, check mail.groups linked to + this user group, and subscribe / unsubscribe them from the discussion + group. This is done by overriding the write method. + """ + _name = 'res.users' + _inherit = ['res.users', 'mail.thread'] + + def write(self, cr, uid, ids, vals, context=None): + write_res = super(res_users_mail_group, self).write(cr, uid, ids, vals, context=context) + if vals.get('groups_id'): + # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} + user_group_ids = [command[1] for command in vals['groups_id'] if command[0] == 4] + user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]] + mail_group_obj = self.pool.get('mail.group') + mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', user_group_ids)], context=context) + mail_group_obj.message_subscribe(cr, uid, mail_group_ids, ids, context=context) + return write_res + + +class res_groups_mail_group(osv.osv): + """ Update of res.groups class + - if adding/removing users from a group, check mail.groups linked to + this user group, and subscribe / unsubscribe them from the discussion + group. This is done by overriding the write method. + """ + _name = 'res.groups' + _inherit = 'res.groups' + + def write(self, cr, uid, ids, vals, context=None): + if vals.get('users'): + # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} + user_ids = [command[1] for command in vals['users'] if command[0] == 4] + user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]] + mail_group_obj = self.pool.get('mail.group') + mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', ids)], context=context) + mail_group_obj.message_subscribe(cr, uid, mail_group_ids, user_ids, context=context) + return super(res_groups_mail_group, self).write(cr, uid, ids, vals, context=context) diff --git a/addons/mail/res_users_view.xml b/addons/mail/res_users_view.xml index 15d32a29992..e268997ac2c 100644 --- a/addons/mail/res_users_view.xml +++ b/addons/mail/res_users_view.xml @@ -27,7 +27,7 @@ <field name="notification_email_pref"/> </field> <xpath expr="/form/sheet" position="after"> - <div class="oe_bottom"> + <div class="oe_chatter oe_mail_group_footer"> <field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/> </div> </xpath> diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index 011e8b27dc5..1fb909f4435 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -1,7 +1,7 @@ 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,1,0
-access_mail_message_all,mail.message.all,model_mail_message,base.group_user,1,1,1,1
+access_mail_message_all,mail.message.all,model_mail_message,,1,0,0,0
+access_mail_message_group_user,mail.message.group.user,model_mail_message,base.group_user,1,1,1,1
access_mail_thread,mail.thread,model_mail_thread,base.group_user,1,1,1,0
-access_mail_subscription,mail.subscription,model_mail_subscription,,1,1,1,1
-access_mail_notification,mail.notification,model_mail_notification,,1,1,1,1
+access_mail_subscription_all,mail.subscription.all,model_mail_subscription,,1,1,1,1
+access_mail_notification_all,mail.notification.all,model_mail_notification,,1,1,1,1
access_mail_group,mail.group,model_mail_group,base.group_user,1,1,1,1
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 0cf932f9a25..03f1078e6d5 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -1,7 +1,6 @@ - -/* ------------------------------ */ -/* Wall */ -/* ------------------------------ */ +/* ------------------------------------------------------------ */ +/* Wall +/* ------------------------------------------------------------ */ .openerp div.oe_mail_wall { overflow: auto; @@ -9,6 +8,17 @@ background: white; } +.openerp div.oe_mail_wall_main { + float: left; + width: 560px; + margin: 8px; +} + +.openerp div.oe_mail_wall_aside { + margin-left: 565px; + margin: 8px; +} + .openerp div.oe_mail_wall_action { padding: 8px; background: #eee; @@ -21,36 +31,32 @@ clear: both; } -.openerp .oe_mail_wall_action textarea { +.openerp div.oe_mail_wall_action .oe_mail_msg_content { + width: 484px; +} + +.openerp div.oe_mail_wall_action textarea.oe_mail_compose_textarea, +.openerp div.oe_mail_wall_action div.oe_mail_compose_message_body_text textarea { width: 474px; height: 60px; padding: 4px; - margin-bottom: 8px; - float: right; + margin-bottom: 2px; } -/* 2 columns view */ -.openerp div.oe_mail_wall_main { - float: left; - width: 560px; - margin: 8px; +.openerp ul.oe_mail_wall_threads { + margin-top: 8px; } -.openerp div.oe_mail_wall_aside { - margin-left: 565px; - margin: 8px; -} +/* Specific display of threads in the wall */ +/* ------------------------------------------------------------ */ -/* Threads */ -.openerp .oe_mail_wall_threads { - margin-top: 8px; -} -.openerp .oe_mail_wall_threads textarea { - height: 40px; +.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea { + width: 434px; + height: 30px; padding: 4px; } -.openerp .oe_mail_wall_thread:first .oe_mail_msg_notification { +.openerp li.oe_mail_wall_thread:first .oe_mail_msg_notification { border-top: 0; } @@ -64,7 +70,7 @@ width: 486px; } -.openerp div.oe_mail_msg_content li{ +.openerp div.oe_mail_msg_content li { float: left; margin-right: 3px; } @@ -79,40 +85,28 @@ } -/* ------------------------------ */ -/* RecordThread */ -/* ------------------------------ */ +/* ------------------------------------------------------------ */ +/* RecordThread +/* ------------------------------------------------------------ */ .openerp div.oe_mail_recthread { overflow: auto; } -/* Left-side CSS */ -.openerp div.oe_mail_recthread_actions { - margin-bottom: 8px; -} - -.openerp div.oe_mail_recthread_followers { - margin-bottom: 8px; -} - -.openerp div.oe_mail_recthread_followers img.oe_mail_msg_image { - width: 28px; - height: 28px; - margin: 4px; -} - -/* RecordThread: 2 columns view */ -.openerp div.oe_mail_recthread_left { +.openerp div.oe_mail_recthread_main { float: left; width: 560px; } -.openerp div.oe_mail_recthread_right { +.openerp div.oe_mail_recthread_aside { float: right; width: 250px; } +.openerp div.oe_mail_recthread_actions { + margin-bottom: 8px; +} + .openerp div.oe_mail_recthread_actions button { width: 120px; } @@ -144,16 +138,17 @@ background-image: linear-gradient(to bottom, #dc5f59, #b33630); } -.openerp textarea.oe_mail_action_textarea { - height: 60px; - padding: 5px; +.openerp div.oe_mail_recthread_followers { + margin-bottom: 8px; } -/* ------------------------------ */ -/* ThreadDisplay */ -/* ------------------------------ */ + +/* ------------------------------------------------------------ */ +/* Thread +/* ------------------------------------------------------------ */ .openerp div.oe_mail_thread_action { + display: none; white-space: normal; padding: 8px; background: #eee; @@ -166,6 +161,26 @@ clear: both; } +/* default textarea (oe_mail_compose_textarea), and body_text textarea for compose form view */ +.openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea, +.openerp .oe_mail_msg_content div.oe_mail_compose_message_body_text textarea { + width: 474px; + height: 60px; + padding: 4px; + font-size: 12px; + border: 1px solid #cccccc; +} + +/* default textarea (oe_mail_compose_textarea), and body_text textarea for compose form view */ +.openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea:focus, +.openerp .oe_mail_msg_content div.oe_mail_compose_message_body_text textarea:focus { + outline: 0; + border-color: rgba(82, 168, 236, 0.8); + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); + -box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); +} + .openerp div.oe_mail_thread_display { white-space: normal; } @@ -174,7 +189,7 @@ margin-left: 66px; } -.openerp div.oe_mail_thread_subthread .oe_mail_thread_msg:last-child { +.openerp div.oe_mail_thread_subthread li.oe_mail_thread_msg:last-child { margin-bottom: 8px; } @@ -183,18 +198,23 @@ border-bottom: 1px solid #D2D9E7; } -.openerp .oe_mail_thread_msg:after { +.openerp li.oe_mail_thread_msg:after { content: ""; display: block; clear: both; } -.openerp .oe_mail_thread_msg > div:after { +.openerp li.oe_mail_thread_msg > div:after { content: ""; display: block; clear: both; } +.openerp div.oe_mail_msg { + padding: 0; + margin: 0 0 4px 0; +} + .openerp .oe_mail_msg_notification, .openerp .oe_mail_msg_comment, .openerp .oe_mail_msg_email { @@ -203,11 +223,6 @@ border-top: 1px solid #ccc; } -.openerp .oe_email_icon { - width: 50px; - height: 50px; -} - .openerp div.oe_mail_thread_subthread .oe_mail_msg_comment { background: #eee; } @@ -230,9 +245,18 @@ clear: both; } -.openerp img.oe_mail_msg_image { +.openerp img.oe_mail_icon { width: 50px; height: 50px; +} + +.openerp img.oe_mail_thumbnail { + width: 28px; + height: 28px; + margin: 4px; +} + +.openerp img.oe_mail_frame { text-align: center; overflow: hidden; -moz-border-radius: 3px; @@ -247,48 +271,60 @@ clip: rect(5px, 40px, 45px, 0px); } -/* ------------------------------ */ -/* Styling (should be openerp) */ -/* ------------------------------ */ +.openerp .oe_mail_invisible { + display: none; +} -.openerp input.oe_mail, textarea.oe_mail { - width: 432px; - padding: 4px; - font-size: 12px; - border: 1px solid #cccccc; - -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; - -moz-transition: border linear 0.2s, box-shadow linear 0.2s; - -ms-transition: border linear 0.2s, box-shadow linear 0.2s; - -o-transition: border linear 0.2s, box-shadow linear 0.2s; - transition: border linear 0.2s, box-shadow linear 0.2s; - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; +/* ------------------------------------------------------------ */ +/* mail.compose.message form view & OpenERP hacks +/* ------------------------------------------------------------ */ + +/* form_view: delete white background */ +.openerp .oe_mail_msg_content div.oe_formview { + background-color: transparent; } -.openerp input.oe_mail:focus, textarea.oe_mail:focus { - outline: 0; - border-color: rgba(82, 168, 236, 0.8); - -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); - -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); - -box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); +.openerp .oe_mail_msg_content div.oe_form_nosheet { + margin: 0px; } -.openerp div.oe_mail_msg { - padding: 0; - margin: 0 0 4px 0; +.openerp .oe_mail_msg_content table.oe_form_group { + margin: 0px; } -.openerp .oe_mail_oe_bold { - font-weight: bold; +.openerp .oe_mail_msg_content table.oe_form_field, +.openerp .oe_mail_msg_content div.oe_form_field { + padding: 0px; +} + +.openerp .oe_mail_msg_content td.oe_form_group_cell { + vertical-align: bottom; +} + +/* subject: change width */ +.openerp .oe_mail_msg_content .oe_form .oe_form_field input { + width: 472px; } -/* ------------------------------ */ -/* Messages layout */ -/* ------------------------------ */ +/* body_html: cleditor */ +.openerp .oe_mail_msg_content div.cleditorMain { + border: 1px solid #cccccc; +} + +/* destination_partner_ids */ +.openerp .oe_mail_msg_content div.text-core { + height: 22px !important; + width: 472px; +} + +/* buttons */ +.openerp .oe_mail_msg_content .oe_mail_compose_message_icons button.oe_form_button { + padding: 1px; +} + +/* ------------------------------------------------------------ */ +/* Messages layout +/* ------------------------------------------------------------ */ .openerp .oe_mail_msg .oe_mail_msg_title { margin: 0; @@ -318,6 +354,10 @@ display: inline; } +.openerp .oe_mail_oe_bold { + font-weight: bold; +} + /* Read more/less link */ .openerp .oe_mail_msg_content .expand, .openerp .oe_mail_msg_content .reduce { @@ -360,35 +400,3 @@ padding: 0; list-style-type: square; } - - -/* ------------------------------ */ -/* Group Form */ -/* ------------------------------ */ - -div.oe_mail_group_advanced_details { - display: none; -} - -.oe_form_sheetbg.openerp_mail_group_sheet { - min-height: 0px; - max-height: none; -} - -.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_sheet { - min-height: 0px; - max-height: none; - padding: 0px 18px; - max-width: 80%; -} - -/* Resize group logo */ -.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_image > img { - max-width: 100px; - max-height: 100px; -} - -/* Resize group description */ -.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_text > textarea { - height: 40px; -} diff --git a/addons/mail/static/src/css/mail_compose_message.css b/addons/mail/static/src/css/mail_compose_message.css new file mode 100644 index 00000000000..4cd1339ded4 --- /dev/null +++ b/addons/mail/static/src/css/mail_compose_message.css @@ -0,0 +1,16 @@ +/* ------------------------------ */ +/* Compose Message Wizard Form */ +/* ------------------------------ */ + +.openerp tr td .oe_form_field.oe_mail_compose_message_invisible { + display: none; +} + +.openerp .oe_mail_compose_message_icons { + text-align: right; +} + +.openerp .oe_mail_compose_message_icons img { + width: 20px; + height: 20px; +} diff --git a/addons/mail/static/src/img/_fp.png b/addons/mail/static/src/img/_fp.png Binary files differdeleted file mode 100644 index 17dce2a047c..00000000000 --- a/addons/mail/static/src/img/_fp.png +++ /dev/null diff --git a/addons/mail/static/src/img/attachment.png b/addons/mail/static/src/img/attachment.png Binary files differnew file mode 100644 index 00000000000..5cc0c33257d --- /dev/null +++ b/addons/mail/static/src/img/attachment.png diff --git a/addons/mail/static/src/img/checklist.png b/addons/mail/static/src/img/checklist.png Binary files differnew file mode 100644 index 00000000000..d252606ff53 --- /dev/null +++ b/addons/mail/static/src/img/checklist.png diff --git a/addons/mail/static/src/img/feeds-hover.png b/addons/mail/static/src/img/feeds-hover.png Binary files differdeleted file mode 100644 index 67070d3e7df..00000000000 --- a/addons/mail/static/src/img/feeds-hover.png +++ /dev/null diff --git a/addons/mail/static/src/img/feeds.png b/addons/mail/static/src/img/feeds.png Binary files differdeleted file mode 100644 index ac2293c0bf5..00000000000 --- a/addons/mail/static/src/img/feeds.png +++ /dev/null diff --git a/addons/mail/static/src/img/formatting.png b/addons/mail/static/src/img/formatting.png Binary files differnew file mode 100644 index 00000000000..cf45fdf192f --- /dev/null +++ b/addons/mail/static/src/img/formatting.png diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 2140bbe474a..8ce050fd144 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -5,210 +5,652 @@ openerp.mail = function(session) { var mail = session.mail = {}; /** - * Add records to sorted_comments array - * @param {Array} records records from mail.message sorted by date desc - * @returns {Object} cs comments_structure: dict - * cs.model_to_root_ids = {model: [root_ids], } - * cs.new_root_ids = [new_root_ids] - * cs.root_ids = [root_ids] - * cs.msgs = {record.id: record,} - * cs.tree_struct = {record.id: { - * 'level': record_level in hierarchy, 0 is root, - * 'msg_nbr': number of childs, - * 'direct_childs': [msg_ids], - * 'all_childs': [msg_ids], - * 'for_thread_msgs': [records], - * 'ancestors': [msg_ids], } } + * ------------------------------------------------------------ + * FormView + * ------------------------------------------------------------ + * + * Override of formview do_action method, to catch all return action about + * mail.compose.message. The purpose is to bind 'Send by e-mail' buttons + * and redirect them to the Chatter. */ - function tools_sort_comments(cs, records, parent_id) { - var cur_iter = 0; var max_iter = 10; var modif = true; - while ( modif && (cur_iter++) < max_iter) { - modif = false; - _(records).each(function (record) { - // root and not yet recorded - if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) { - // add to model -> root_list ids - if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id]; - else cs['model_to_root_ids'][record.model].push(record.id); - // add root data - cs['new_root_ids'].push(record.id); - // add record - cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []}; - cs['msgs'][record.id] = record; - modif = true; - } - // not yet recorded, but parent is recorded - else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) { - var parent_level = cs['tree_struct'][record.parent_id[0]]['level']; - // update parent structure - cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id); - cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record); - // update ancestors structure - for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) { - cs['tree_struct'][ancestor_id]['all_childs'].push(record.id); + + session.web.FormView = session.web.FormView.extend({ + do_action: function(action, on_close) { + if (action.res_model == 'mail.compose.message' && this.fields && this.fields.message_ids) { + var record_thread = this.fields.message_ids; + var thread = record_thread.thread; + thread.instantiate_composition_form('comment', true, false, 0, action.context); + return false; + } + else { + return this._super(action, on_close); + } + }, + }); + + + /** + * ------------------------------------------------------------ + * ChatterUtils + * ------------------------------------------------------------ + * + * This class holds a few tools method that will be used by + * the various Chatter widgets. + */ + + mail.ChatterUtils = { + + /** + * mail_int_mapping: structure to keep a trace of internal links mapping + * mail_int_mapping['model'] = { + * 'name_get': [[id,label], [id,label], ...] + * 'fetch_ids': [id, id, ...] } */ + //var mail_int_mapping = {}; + + /** + * mail_msg_struct: structure to orrganize chatter messages + */ + //var mail_msg_struct = {}; // TODO: USE IT OR NOT :) + + /* generic chatter events binding */ + bind_events: function(widget) { + // event: click on an internal link to a document: model, login + widget.$element.delegate('a.oe_mail_internal_link', 'click', function (event) { + event.preventDefault(); + // lazy implementation: fetch data and try to redirect + if (! event.srcElement.dataset.resModel) return false; + else var res_model = event.srcElement.dataset.resModel; + var res_login = event.srcElement.dataset.resLogin; + if (! res_login) return false; + var ds = new session.web.DataSet(widget, res_model); + var defer = ds.call('search', [[['login', '=', res_login]]]).pipe(function (records) { + if (records[0]) { + widget.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(records[0]), views: [[false, 'form']]}); + } + else return false; + }); + }); + }, + + /** get an image in /web/binary/image?... */ + get_image: function(session_prefix, session_id, model, field, id) { + return session_prefix + '/web/binary/image?session_id=' + session_id + '&model=' + model + '&field=' + field + '&id=' + (id || ''); + }, + + /** checks if tue current user is the message author */ + is_author: function (widget, message_user_id) { + return (widget.session && widget.session.uid != 0 && widget.session.uid == message_user_id); + }, + + /** + * Add records to comments_structure array + * @param {Array} records records from mail.message sorted by date desc + * @returns {Object} cs comments_structure: dict + * cs.model_to_root_ids = {model: [root_ids], } + * cs.new_root_ids = [new_root_ids] + * cs.root_ids = [root_ids] + * cs.msgs = {record.id: record,} + * cs.tree_struct = {record.id: { + * 'level': record_level in hierarchy, 0 is root, + * 'msg_nbr': number of childs, + * 'direct_childs': [msg_ids], + * 'all_childs': [msg_ids], + * 'for_thread_msgs': [records], + * 'ancestors': [msg_ids], } } + */ + records_struct_add_records: function(cs, records, parent_id) { + var cur_iter = 0; var max_iter = 10; var modif = true; + while ( modif && (cur_iter++) < max_iter) { + modif = false; + _(records).each(function (record) { + // root and not yet recorded + if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) { + // add to model -> root_list ids + if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id]; + else cs['model_to_root_ids'][record.model].push(record.id); + // add root data + cs['new_root_ids'].push(record.id); + // add record + cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []}; + cs['msgs'][record.id] = record; + modif = true; + } + // not yet recorded, but parent is recorded + else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) { + var parent_level = cs['tree_struct'][record.parent_id[0]]['level']; + // update parent structure + cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id); + cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record); + // update ancestors structure + for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) { + cs['tree_struct'][ancestor_id]['all_childs'].push(record.id); + } + // add record + cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []}; + cs['msgs'][record.id] = record; + modif = true; } - // add record - cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []}; - cs['msgs'][record.id] = record; - modif = true; + }); + } + return cs; + }, + + /* copy cs.new_root_ids into cs.root_ids */ + records_struct_update_after_display: function(cs) { + // update TODO + cs['root_ids'] = _.union(cs['root_ids'], cs['new_root_ids']); + cs['new_root_ids'] = []; + return cs; + }, + + /** + * CONTENT MANIPULATION + * + * Regular expressions + * - (^|\s)@((\w|@|\.)*): @login@log.log, supports inner '@' for + * logins that are emails + * 1. '(void)' + * 2. login@log.log + * - (^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]: [ir.attachment,3|My Label], + * for internal links to model ir.attachment, id=3, and with + * optional label 'My Label'. Note that having a '|Label' is not + * mandatory, because the regex should still be correct. + * 1. '(void)' + * 2. 'ir' + * 3. 'attachment' + * 4. '3' + * 5. 'My Label' + */ + + /** Removes html tags, except b, em, br, ul, li */ + do_text_remove_html_tags: function (string) { + var html = $('<div/>').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('<(/)?(b|em|br|br /|ul|li|div)\\s*>', 'gi'), '<$1$2>'); + return html; + }, + + /** Replaces line breaks by html line breaks (br) */ + do_text_nl2br: function (str, is_xhtml) { + var break_tag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>'; + return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2'); + }, + + /* Add a prefix before each new line of the original string */ + do_text_quote: function (str, prefix) { + return str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2' + prefix || '> '); + }, + + /** + * Replaces some expressions + * - @login - shorcut to link to a res.user, given its login + * - [ir.attachment,3|My Label] - shortcut to an internal + * document + * - :name - shortcut to an image + */ + do_replace_expressions: function (string) { + var self = this; + var icon_list = ['al', 'pinky'] + /* shortcut to user: @login */ + var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); + var regex_res = regex_login.exec(string); + while (regex_res != null) { + var login = regex_res[2]; + string = string.replace(regex_res[0], regex_res[1] + '<a href="#" class="oe_mail_internal_link" data-res-model="res.users" data-res-login = ' + login + '>@' + login + '</a>'); + regex_res = regex_login.exec(string); + } + /* shortcut for internal document */ + var regex_login = new RegExp(/(^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]/g); + var regex_res = regex_login.exec(string); + while (regex_res != null) { + var res_model = regex_res[2] + '.' + regex_res[3]; + var res_id = regex_res[4]; + if (! regex_res[5]) { + var label = res_model + ':' + res_id } + else { + var label = regex_res[5]; } + string = string.replace(regex_res[0], regex_res[1] + '<a href="#model=' + res_model + '&id=' + res_id + '>' + label + '</a>'); + regex_res = regex_login.exec(string); + } + /* special shortcut: :name, try to find an icon if in list */ + var regex_login = new RegExp(/(^|\s):((\w)*)/g); + var regex_res = regex_login.exec(string); + while (regex_res != null) { + var icon_name = regex_res[2]; + if (_.include(icon_list, icon_name)) + string = string.replace(regex_res[0], regex_res[1] + '<img src="/mail/static/src/img/_' + icon_name + '.png" width="22px" height="22px" alt="' + icon_name + '"/>'); + regex_res = regex_login.exec(string); + } + return string; + }, + + /** + * Checks a string to find an expression that will be replaced + * by an internal link and requiring a name_get to replace + * the expression. + * :param mapping: structure to keep a trace of internal links mapping + * mapping['model'] = { + * name_get': [[id,label], [id,label], ...] + * 'to_fetch_ids': [id, id, ...] + * } + * CURRENTLY NOT IMPLEMENTED */ + do_check_for_name_get_mapping: function(string, mapping) { + /* shortcut to user: @login */ + //var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); + //var regex_res = regex_login.exec(string); + //while (regex_res != null) { + //var login = regex_res[2]; + //if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; } + //this.map_hash['res.users']['login'].push(login); + //regex_res = regex_login.exec(string); + //} + /* document link with name_get: [res.model,name] */ + /* internal link with id: [res.model,id], or [res.model,id|display_name] */ + //var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); + //regex_res = regex_intlink.exec(string); + //while (regex_res != null) { + //var res_model = regex_res[2] + '.' + regex_res[3]; + //var res_name = regex_res[4]; + //if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; } + //this.map_hash[res_model]['name'].push(res_name); + //regex_res = regex_intlink.exec(string); + //} + }, + + /** + * Updates the mapping; check for to_fetch_ids for each recorded + * model, and perform a name_get to update the mapping. + * CURRENTLY NOT IMPLEMENTED */ + do_update_name_get_mapping: function(mapping) { + }, + }; + + + /** + * ------------------------------------------------------------ + * ComposeMessage widget + * ------------------------------------------------------------ + * + * This widget handles the display of a form to compose a new message. + * This form is an OpenERP form_view, build on a mail.compose.message + * wizard. + */ + + mail.ComposeMessage = session.web.Widget.extend({ + template: 'mail.compose_message', + + /** + * @param {Object} parent parent + * @param {Object} [params] + * @param {String} [params.res_model] res_model of document [REQUIRED] + * @param {Number} [params.res_id] res_id of record [REQUIRED] + * @param {Number} [params.email_mode] true/false, tells whether + * we are in email sending mode + * @param {Number} [params.formatting] true/false, tells whether + * we are in advance formatting mode + * @param {String} [params.model] mail.compose.message.mode (see + * composition wizard) + * @param {Number} [params.msg_id] id of a message in case we are in + * reply mode + */ + init: function(parent, params) { + var self = this; + this._super(parent); + // options + this.params = params || {}; + this.params.context = params.context || {}; + this.params.email_mode = params.email_mode || false; + this.params.formatting = params.formatting || false; + this.params.mode = params.mode || 'comment'; + this.params.form_xml_id = params.form_xml_id || 'email_compose_message_wizard_form_chatter'; + this.params.form_view_id = false; + if (this.params.mode == 'reply') { + this.params.active_id = this.params.msg_id; + } else { + this.params.active_id = this.params.res_id; + } + this.email_mode = false; + this.formatting = false; + }, + + /** + * Reinitialize the widget field values to the default values. The + * purpose is to avoid to destroy and re-build a form view. Default + * values are therefore given as for an onchange. */ + reinit: function() { + var self = this; + if (! this.form_view) return; + var call_defer = this.ds_compose.call('default_get', [['subject', 'body_text', 'body_html', 'dest_partner_ids'], this.ds_compose.get_context()]).then( + function (result) { + self.form_view.on_processed_onchange({'value': result}, []); + }); + return call_defer; + }, + + /** + * Override-hack of do_action: clean the form */ + do_action: function(action, on_close) { + // this.init_comments(); + return this._super(action, on_close); + }, + + /** + * Widget start function + * - builds and initializes the form view */ + start: function() { + var self = this; + this._super.apply(this, arguments); + // customize display: add avatar, clean previous content + var user_avatar = mail.ChatterUtils.get_image(this.session.prefix, + this.session.session_id, 'res.users', 'avatar', this.session.uid); + this.$element.find('img.oe_mail_icon').attr('src', user_avatar); + this.$element.find('div.oe_mail_msg_content').empty(); + // create a context for the default_get of the compose form + var widget_context = { + 'active_model': this.params.res_model, + 'active_id': this.params.active_id, + 'mail.compose.message.mode': this.params.mode, + }; + var context = _.extend({}, this.params.context, widget_context); + this.ds_compose = new session.web.DataSetSearch(this, 'mail.compose.message', context); + // find the id of the view to display in the chatter form + var data_ds = new session.web.DataSetSearch(this, 'ir.model.data'); + var deferred_form_id =data_ds.call('get_object_reference', ['mail', this.params.form_xml_id]).then( function (result) { + if (result) { + self.params.form_view_id = result[1]; + } + }).pipe(this.proxy('create_form_view')); + return deferred_form_id; + }, + + /** + * Create a FormView, then append it to the to widget DOM. */ + create_form_view: function () { + var self = this; + // destroy previous form_view if any + if (this.form_view) { this.form_view.destroy(); } + // create the FormView + this.form_view = new session.web.FormView(this, this.ds_compose, this.params.form_view_id, { + action_buttons: false, + pager: false, + initial_mode: 'edit', + }); + // add the form, bind events, activate the form + var msg_node = this.$element.find('div.oe_mail_msg_content'); + return $.when(this.form_view.appendTo(msg_node)).pipe(function() { + self.bind_events(); + self.form_view.do_show(); + if (self.params.email_mode) { self.toggle_email_mode(); } + if (self.params.formatting) { self.toggle_formatting_mode(); } + }); + }, + + destroy: function() { + this._super.apply(this, arguments); + }, + + /** + * Bind events in the widget. Each event is slightly described + * in the function. */ + bind_events: function() { + var self = this; + this.$element.find('button.oe_form_button').click(function (event) { + event.preventDefault(); + }); + // event: click on 'Send an Email' link that toggles the form for + // sending an email (partner_ids) + this.$element.find('a.oe_mail_compose_message_email').click(function (event) { + event.preventDefault(); + self.toggle_email_mode(); + }); + // event: click on 'Formatting' icon-link that toggles the advanced + // formatting options for writing a message (subject, body_html) + this.$element.find('a.oe_mail_compose_message_formatting').click(function (event) { + event.preventDefault(); + self.toggle_formatting_mode(); + }); + // event: click on 'Attachment' icon-link that opens the dialog to + // add an attachment. + this.$element.find('a.oe_mail_compose_message_attachment').click(function (event) { + event.preventDefault(); + // not yet implemented + self.set_body_value('attachment', 'attachment'); }); - } - return cs; - } + // event: click on 'Checklist' icon-link that toggles the options + // for adding checklist. + this.$element.find('a.oe_mail_compose_message_checklist').click(function (event) { + event.preventDefault(); + // not yet implemented + self.set_body_value('checklist', 'checklist'); + }); + }, + + /** + * Toggle the formatting mode. */ + toggle_formatting_mode: function() { + var self = this; + this.formatting = ! this.formatting; + // calls onchange + var call_defer = this.ds_compose.call('onchange_formatting', [[], this.formatting, this.params.res_model, this.params.res_id]).then( + function (result) { + self.form_view.on_processed_onchange(result, []); + }); + // update context of datasetsearch + this.ds_compose.context.formatting = this.formatting; + // toggle display + this.$element.find('span.oe_mail_compose_message_subject').toggleClass('oe_mail_compose_message_invisible'); + this.$element.find('div.oe_mail_compose_message_body_text').toggleClass('oe_mail_compose_message_invisible'); + this.$element.find('div.oe_mail_compose_message_body_html').toggleClass('oe_mail_compose_message_invisible'); + }, + + /** + * Toggle the email mode. */ + toggle_email_mode: function() { + var self = this; + this.email_mode = ! this.email_mode; + // calls onchange + var call_defer = this.ds_compose.call('onchange_email_mode', [[], this.email_mode, this.params.res_model, this.params.res_id]).then( + function (result) { + self.form_view.on_processed_onchange(result, []); + }); + // update context of datasetsearch + this.ds_compose.context.email_mode = this.email_mode; + // update 'Post' button -> 'Send' + // update 'Send an Email' link -> 'Post a comment' + if (this.email_mode) { + this.$element.find('button.oe_mail_compose_message_button_send').html('<span>Send</span>'); + this.$element.find('a.oe_mail_compose_message_email').html('Comment'); + } else { + this.$element.find('button.oe_mail_compose_message_button_send').html('<span>Post</span>'); + this.$element.find('a.oe_mail_compose_message_email').html('Send an Email'); + } + // toggle display + this.$element.find('div.oe_mail_compose_message_partner_ids').toggleClass('oe_mail_compose_message_invisible'); + }, + + /** + * Update the values of the composition form; with possible different + * values for body_text and body_html. */ + set_body_value: function(body_text, body_html) { + this.form_view.fields.body_text.set_value(body_text); + this.form_view.fields.body_html.set_value(body_html); + }, + }), /** - * ThreadDisplay widget: this widget handles the display of a thread of - * messages. The [thread_level] parameter sets the thread level number: + * ------------------------------------------------------------ + * Thread Widget + * ------------------------------------------------------------ + * + * This widget handles the display of a thread of messages. The + * [thread_level] parameter sets the thread level number: * - root message * - - sub message (parent_id = root message) * - - - sub sub message (parent id = sub message) * - - sub message (parent_id = root message) - * This widget has 2 ways of initialization, either you give records to be rendered, - * either it will fetch [limit] messages related to [res_model]:[res_id]. + * This widget has 2 ways of initialization, either you give records + * to be rendered, either it will fetch [limit] messages related to + * [res_model]:[res_id]. */ + mail.Thread = session.web.Widget.extend({ - template: 'mail.Thread', + template: 'mail.thread', /** * @param {Object} parent parent * @param {Object} [params] - * @param {String} [params.res_model] res_model of mail.thread object - * @param {Number} [params.res_id] res_id of record - * @param {Number} [params.parent_id=false] parent_id of message - * @param {Number} [params.uid] user id - * @param {Number} [params.thread_level=0] number of levels in the thread (only 0 or 1 currently) - * @param {Number} [params.msg_more_limit=100] number of character to display before having a "show more" link; - * note that the text will not be truncated if it does not have 110% of - * the parameter (ex: 110 characters needed to be truncated and be displayed - * as a 100-characters message) - * @param {Number} [params.limit=10] maximum number of messages to fetch + * @param {String} [params.res_model] res_model of document [REQUIRED] + * @param {Number} [params.res_id] res_id of record [REQUIRED] + * @param {Number} [params.uid] user id [REQUIRED] + * @param {Bool} [params.parent_id=false] parent_id of message + * @param {Number} [params.thread_level=0] number of levels in the thread + * (only 0 or 1 currently) + * @param {Bool} [params.is_wall=false] thread is displayed in the wall + * @param {Number} [params.msg_more_limit=150] number of character to + * display before having a "show more" link; note that the text + * will not be truncated if it does not have 110% of the parameter + * (ex: 110 characters needed to be truncated and be displayed as + * a 100-characters message) + * @param {Number} [params.limit=100] maximum number of messages to fetch * @param {Number} [params.offset=0] offset for fetching messages * @param {Number} [params.records=null] records to show instead of fetching messages */ init: function(parent, params) { this._super(parent); + // options this.params = params; this.params.parent_id = this.params.parent_id || false; this.params.thread_level = this.params.thread_level || 0; - this.params.msg_more_limit = this.params.msg_more_limit || 290; + this.params.is_wall = this.params.is_wall || (this.params.records != undefined) || false; + this.params.msg_more_limit = this.params.msg_more_limit || 150; this.params.limit = this.params.limit || 100; + // this.params.limit = 3; // tmp for testing this.params.offset = this.params.offset || 0; this.params.records = this.params.records || null; // datasets and internal vars - this.ds = new session.web.DataSet(this, this.params.res_model); - this.ds_users = new session.web.DataSet(this, 'res.users'); - this.ds_msg = new session.web.DataSet(this, 'mail.message'); - this.sorted_comments = {'root_ids': [], 'root_id_msg_list': {}}; + this.ds = new session.web.DataSetSearch(this, this.params.res_model); + this.ds_users = new session.web.DataSetSearch(this, 'res.users'); + this.ds_msg = new session.web.DataSetSearch(this, 'mail.message'); this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; // display customization vars this.display = {}; this.display.show_post_comment = this.params.show_post_comment || false; - this.display.show_reply = (this.params.thread_level > 0); - this.display.show_delete = true; - this.display.show_hide = this.params.show_hide || false; + this.display.show_reply = (this.params.thread_level > 0 && this.params.is_wall); + this.display.show_delete = ! this.params.is_wall; + this.display.show_hide = this.params.is_wall; + this.display.show_reply_by_email = ! this.params.is_wall; this.display.show_more = (this.params.thread_level == 0); - // not used currently - this.intlinks_mapping = {}; }, start: function() { - var self = this; - this._super.apply(this, arguments); - // customize display - if (! this.display.show_post_comment) { - this.$element.find('div.oe_mail_thread_action').hide(); - } // add events - this.add_events(); - + this.bind_events(); // display user, fetch comments this.display_current_user(); - - if (this.params.records) { - var display_done = this.display_comments_from_parameters(this.params.records); - } else { - var display_done = this.init_comments(); + if (this.params.records) var display_done = this.display_comments_from_parameters(this.params.records); + else var display_done = this.init_comments(); + // customize display + $.when(display_done).then(this.proxy('do_customize_display')); + // add message composition form view + if (this.display.show_post_comment) { + var compose_done = this.instantiate_composition_form(); } - return display_done + return display_done && compose_done; }, - - add_events: function() { + + /** + * Override-hack of do_action: automatically reload the chatter. + * Normally it should be called only when clicking on 'Post/Send' + * in the composition form. */ + do_action: function(action, on_close) { + this.init_comments(); + if (this.compose_message_widget) { + this.compose_message_widget.reinit(); } + return this._super(action, on_close); + }, + + instantiate_composition_form: function(mode, email_mode, formatting, msg_id, context) { + if (this.compose_message_widget) { + this.compose_message_widget.destroy(); + } + this.compose_message_widget = new mail.ComposeMessage(this, { + 'extended_mode': false, 'uid': this.params.uid, 'res_model': this.params.res_model, + 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id, + 'email_mode': email_mode || false, 'formatting': formatting || false, + 'context': context || false } ); + var composition_node = this.$element.find('div.oe_mail_thread_action'); + composition_node.empty(); + var compose_done = this.compose_message_widget.appendTo(composition_node); + return compose_done; + }, + + do_customize_display: function() { + if (this.display.show_post_comment) { this.$element.find('div.oe_mail_thread_action').eq(0).show(); } + }, + + /** + * Bind events in the widget. Each event is slightly described + * in the function. */ + bind_events: function() { var self = this; + // generic events from Chatter Mixin + mail.ChatterUtils.bind_events(this); // event: click on 'more' at bottom of thread this.$element.find('button.oe_mail_button_more').click(function () { self.do_more(); }); - // event: writing in textarea - this.$element.find('textarea.oe_mail_action_textarea').keyup(function (event) { + // event: writing in basic textarea of composition form (quick reply) + this.$element.find('textarea.oe_mail_compose_textarea').keyup(function (event) { var charCode = (event.which) ? event.which : window.event.keyCode; if (event.shiftKey && charCode == 13) { this.value = this.value+"\n"; } else if (charCode == 13) { return self.do_comment(); } }); - // event: click on 'reply' in msg + // event: click on 'Reply' in msg this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply', 'click', function (event) { var act_dom = $(this).parents('div.oe_mail_thread_display').find('div.oe_mail_thread_action:first'); act_dom.toggle(); event.preventDefault(); }); - // event: click on 'delete' in msg + // event: click on 'attachment(s)' in msg + this.$element.delegate('a.oe_mail_msg_view_attachments', 'click', function (event) { + var act_dom = $(this).parent().parent().parent().find('.oe_mail_msg_attachments'); + act_dom.toggle(); + event.preventDefault(); + }); + // event: click on 'Delete' in msg side menu this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_delete', 'click', function (event) { - //console.log('deleting'); if (! confirm(_t("Do you really want to delete this message?"))) { return false; } var msg_id = event.srcElement.dataset.id; if (! msg_id) return false; var call_defer = self.ds_msg.unlink([parseInt(msg_id)]); - $(event.srcElement).parents('.oe_mail_thread_msg').eq(0).hide(); + $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide(); if (self.params.thread_level > 0) { $(event.srcElement).parents('.oe_mail_thread').eq(0).hide(); } - return false; + event.preventDefault(); + return call_defer; }); - // event: click on 'hide' in msg + // event: click on 'Hide' in msg side menu this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_hide', 'click', function (event) { - //console.log('hiding'); if (! confirm(_t("Do you really want to hide this thread ?"))) { return false; } var msg_id = event.srcElement.dataset.id; if (! msg_id) return false; - //console.log(msg_id); var call_defer = self.ds.call('message_remove_pushed_notifications', [[self.params.res_id], [parseInt(msg_id)], true]); - $(event.srcElement).parents('.oe_mail_thread_msg').eq(0).hide(); + $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide(); if (self.params.thread_level > 0) { $(event.srcElement).parents('.oe_mail_thread').eq(0).hide(); } - return false; - }); - // event: click on an internal link - this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_internal_link', 'click', function (event) { - // lazy implementation: fetch data and try to redirect - if (! event.srcElement.dataset.resModel) return false; - else var res_model = event.srcElement.dataset.resModel; - var res_login = event.srcElement.dataset.resLogin; - var res_id = event.srcElement.dataset.resId; - if ((! res_login) && (! res_id)) return false; - if (! res_id) { - var ds = new session.web.DataSet(self, res_model); - var defer = ds.call('search', [[['login', '=', res_login]]]).then(function (records) { - if (records[0]) { - self.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(records[0]), views: [[false, 'form']]}); - } - else return false; - }); - } - else self.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(res_id), views: [[false, 'form']]}); - }); - // event: click on 'attachment(s)' in msg - this.$element.delegate('a.oe_mail_msg_view_attachments', 'click', function (event) { - var act_dom = $(this).parent().parent().parent().find('.oe_mail_msg_attachments'); - act_dom.toggle(); - return false; + event.preventDefault(); + return call_defer; }); - // see more - this.$element.on('click','a.oe_mail_msg_more', function (event) { - $(this).siblings('.oe_mail_msg_tail').show(); - $(this).hide(); - return false; + // event: click on "Reply" in msg side menu (email style) + this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply_by_email', 'click', function (event) { + var msg_id = event.srcElement.dataset.msg_id; + var email_mode = (event.srcElement.dataset.type == 'email'); + var formatting = (event.srcElement.dataset.formatting == 'html'); + if (! msg_id) return false; + self.instantiate_composition_form('reply', email_mode, formatting, msg_id); + event.preventDefault(); }); }, @@ -219,21 +661,22 @@ openerp.mail = function(session) { init_comments: function() { var self = this; this.params.offset = 0; - this.sorted_comments = {'root_ids': [], 'root_id_msg_list': {}}; this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; this.$element.find('div.oe_mail_thread_display').empty(); - domain = this.get_fetch_domain(this.sorted_comments); + var domain = this.get_fetch_domain(this.comments_structure); return this.fetch_comments(this.params.limit, this.params.offset, domain).then(); }, fetch_comments: function (limit, offset, domain) { var self = this; - var defer = this.ds.call('message_load', [[this.params.res_id], ( (limit+1)||(this.params.limit+1) ), (offset||this.params.offset), (domain||[]), (this.params.thread_level > 0), (this.sorted_comments['root_ids'])]); - $.when(defer).then(function (records) { + var defer = this.ds.call('message_read', [[this.params.res_id], (this.params.thread_level > 0), (this.comments_structure['root_ids']), + (limit+1) || (this.params.limit+1), offset||this.params.offset, domain||undefined ]).then(function (records) { if (records.length <= self.params.limit) self.display.show_more = false; - else { self.display.show_more = true; records.pop(); } - + // else { self.display.show_more = true; records.pop(); } + // else { self.display.show_more = true; records.splice(0, 1); } + else { self.display.show_more = true; } self.display_comments(records); + // TODO: move to customize display if (self.display.show_more == true) self.$element.find('div.oe_mail_thread_more:last').show(); else self.$element.find('div.oe_mail_thread_more:last').hide(); }); @@ -245,6 +688,7 @@ openerp.mail = function(session) { if (records.length > 0 && records.length < (records[0].child_ids.length+1) ) this.display.show_more = true; else this.display.show_more = false; var defer = this.display_comments(records); + // TODO: move to customize display if (this.display.show_more == true) $('div.oe_mail_thread_more').eq(-2).show(); else $('div.oe_mail_thread_more').eq(-2).hide(); return defer; @@ -252,7 +696,8 @@ openerp.mail = function(session) { display_comments: function (records) { var self = this; - + // sort the records + mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, this.params.parent_id); //build attachments download urls and compute time-relative from dates for (var k in records) { records[k].timerelative = $.timeago(records[k].date); @@ -263,11 +708,10 @@ openerp.mail = function(session) { } } } - this.cs = this.sort_comments_tmp(records); _(records).each(function (record) { var sub_msgs = []; if ((record.parent_id == false || record.parent_id[0] == self.params.parent_id) && self.params.thread_level > 0 ) { - var sub_list = self.cs['tree_struct'][record.id]['direct_childs']; + var sub_list = self.comments_structure['tree_struct'][record.id]['direct_childs']; _(records).each(function (record) { //if (record.parent_id == false || record.parent_id[0] == self.params.parent_id) return; if (_.indexOf(sub_list, record.id) != -1) { @@ -276,35 +720,40 @@ openerp.mail = function(session) { }); self.display_comment(record); self.thread = new mail.Thread(self, {'res_model': self.params.res_model, 'res_id': self.params.res_id, 'uid': self.params.uid, - 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id}); - self.$element.find('.oe_mail_thread_msg:last').append('<div class="oe_mail_thread_subthread"/>'); + 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id, + 'is_wall': self.params.is_wall}); + self.$element.find('li.oe_mail_thread_msg:last').append('<div class="oe_mail_thread_subthread"/>'); self.thread.appendTo(self.$element.find('div.oe_mail_thread_subthread:last')); } else if (self.params.thread_level == 0) { self.display_comment(record); } }); + mail.ChatterUtils.records_struct_update_after_display(this.comments_structure); // update offset for "More" buttons if (this.params.thread_level == 0) this.params.offset += records.length; }, - /** - * Display a record - */ + /** Displays a record, performs text/link formatting */ display_comment: function (record) { - record.body = this.do_text_nl2br(record.body, true); + record.body = mail.ChatterUtils.do_text_nl2br(record.body, true); + // if (record.type == 'email' && record.state == 'received') { if (record.type == 'email') { record.mini_url = ('/mail/static/src/img/email_icon.png'); - } else { - record.mini_url = this.thread_get_avatar('res.users', 'avatar', record.user_id[0]); + } else { + record.mini_url = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'avatar', record.user_id[0]); } // body text manipulation - record.body = this.do_clean_text(record.body); - record.body = this.do_replace_internal_links(record.body); - + if (record.subtype == 'plain') { + record.body = mail.ChatterUtils.do_text_remove_html_tags(record.body); + } + record.body = mail.ChatterUtils.do_replace_expressions(record.body); // format date according to the user timezone record.date = session.web.format_value(record.date, {type:"datetime"}); - var rendered = session.web.qweb.render('mail.Thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display}); + // is the user the author ? + record.is_author = mail.ChatterUtils.is_author(this, record.user_id[0]); + // render + var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display}); $(rendered).appendTo(this.$element.children('div.oe_mail_thread_display:first')); // expand feature this.$element.find('div.oe_mail_msg_body:last').expander({ @@ -316,84 +765,33 @@ openerp.mail = function(session) { lesClass: 'oe_mail_reduce', }); }, - - /** - * Add records to sorted_comments array - * @param {Array} records records from mail.message sorted by date desc - * @returns {Object} sc sorted_comments: dict { - * 'root_id_list': list or root_ids - * 'root_id_msg_list': {'record_id': [ancestor_ids]}, still sorted by date desc - * 'id_to_root': {'root_id': [records]}, still sorted by date desc - * } - */ - sort_comments: function (records) { - var self = this; - sc = {'root_id_list': [], 'root_id_msg_list': {}, 'id_to_root': {}} - var cur_iter = 0; var max_iter = 10; var modif = true; - /* step1: get roots */ - while ( modif && (cur_iter++) < max_iter) { - modif = false; - _(records).each(function (record) { - if ( (record.parent_id == false || record.parent_id[0] == self.params.parent_id) && (_.indexOf(sc['root_id_list'], record.id) == -1)) { - sc['root_id_list'].push(record.id); - sc['root_id_msg_list'][record.id] = []; - self.sorted_comments['root_ids'].push(record.id); - modif = true; - } - else { - if (_.indexOf(sc['root_id_list'], record.parent_id[0]) != -1) { - sc['id_to_root'][record.id] = record.parent_id[0]; - modif = true; - } - else if ( sc['id_to_root'][record.parent_id[0]] ) { - sc['id_to_root'][record.id] = sc['id_to_root'][record.parent_id[0]]; - modif = true; - } - } - }); - } - /* step2: add records */ - _(records).each(function (record) { - var root_id = sc['id_to_root'][record.id]; - if (! root_id) return; - sc['root_id_msg_list'][root_id].push(record); - //self.sorted_comments['root_id_msg_list'][root_id].push(record.id); - }); - return sc; - }, - - /** - * Add records to comments_structure object: see function for details - */ - sort_comments_tmp: function(records) { - return tools_sort_comments(this.comments_structure, records, this.params.parent_id); - }, - + display_current_user: function () { - return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.params.uid)); + var avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'avatar', this.params.uid); + return this.$element.find('img.oe_mail_icon').attr('src', avatar); }, do_comment: function () { var comment_node = this.$element.find('textarea'); var body_text = comment_node.val(); comment_node.val(''); - return this.ds.call('message_append_note', [[this.params.res_id], 'Reply', body_text, this.params.parent_id, 'comment', 'html']).then( + return this.ds.call('message_append_note', [[this.params.res_id], '', body_text, this.params.parent_id, 'comment', 'plain']).then( this.proxy('init_comments')); }, /** * Create a domain to fetch new comments according to - * comment already present in sorted_comments - * @param {Object} sorted_comments (see sort_comments) + * comment already present in comments_structure + * @param {Object} comments_structure (see chatter utils) * @returns {Array} fetch_domain (OpenERP domain style) */ - get_fetch_domain: function (sorted_comments) { + get_fetch_domain: function (comments_structure) { var domain = []; - var ids = sorted_comments.root_ids.slice(); + var ids = comments_structure.root_ids.slice(); var ids2 = []; // must be child of current parent if (this.params.parent_id) { domain.push(['id', 'child_of', this.params.parent_id]); } - _(sorted_comments.root_ids).each(function (id) { // each record + _(comments_structure.root_ids).each(function (id) { // each record ids.push(id); ids2.push(id); }); @@ -413,109 +811,46 @@ openerp.mail = function(session) { }, do_more: function () { - domain = this.get_fetch_domain(this.sorted_comments); + domain = this.get_fetch_domain(this.comments_structure); return this.fetch_comments(this.params.limit, this.params.offset, domain); }, - - /** - * - * var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - * var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); - */ - do_replace_internal_links: function (string) { - var self = this; - var icon_list = ['al', 'pinky'] - /* shortcut to user: @login */ - var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var login = regex_res[2]; - string = string.replace(regex_res[0], regex_res[1] + '<a href="#" class="oe_mail_internal_link" data-res-model="res.users" data-res-login = ' + login + '>@' + login + '</a>'); - regex_res = regex_login.exec(string); - } - /* special shortcut: :name, try to find an icon if in list */ - var regex_login = new RegExp(/(^|\s):((\w)*)/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var icon_name = regex_res[2]; - if (_.include(icon_list, icon_name)) - string = string.replace(regex_res[0], regex_res[1] + '<img src="/mail/static/src/img/_' + icon_name + '.png" width="22px" height="22px" alt="' + icon_name + '"/>'); - regex_res = regex_login.exec(string); - } - return string; - }, - - thread_get_avatar: function(model, field, id) { - return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || ''); - }, - - /** Removes html tags, except b, em, br */ - do_clean_text: function (string) { - var html = $('<div/>').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('<(/)?(b|em|br|br /)\\s*>', 'gi'), '<$1$2>'); - return html; - }, - - /** Replaces line bracks by html line breaks (br) */ - do_text_nl2br: function (str, is_xhtml) { - var break_tag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>'; - return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2'); - }, - - /** - * - * var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - * var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); - */ - do_check_internal_links: function(string) { - /* shortcut to user: @login */ - var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var login = regex_res[2]; - if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; } - this.map_hash['res.users']['login'].push(login); - regex_res = regex_login.exec(string); - } - /* internal links: #res.model,name */ - var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); - regex_res = regex_intlink.exec(string); - while (regex_res != null) { - var res_model = regex_res[2] + '.' + regex_res[3]; - var res_name = regex_res[4]; - if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; } - this.map_hash[res_model]['name'].push(res_name); - regex_res = regex_intlink.exec(string); - } - }, - - /** checks if tue current user is the message author */ - _is_author: function (id) { - return (this.session.uid == id); - }, - }); - session.web.form.widgets.add( 'Thread', 'openerp.mail.Thread'); + + + /** + * ------------------------------------------------------------ + * mail_thread Widget + * ------------------------------------------------------------ + * + * This widget handles the display of the Chatter on documents. + */ + + /* Add mail_thread widget to registry */ + session.web.form.widgets.add('mail_thread', 'openerp.mail.RecordThread'); /** mail_thread widget: thread of comments */ mail.RecordThread = session.web.form.AbstractField.extend({ - template: 'mail.RecordThread', + // QWeb template to use when rendering the object + template: 'mail.record_thread', init: function() { this._super.apply(this, arguments); - this.see_subscribers = true; - this.thread = null; this.params = this.get_definition_options(); this.params.thread_level = this.params.thread_level || 0; - // datasets + this.params.see_subscribers = true; + this.params.see_subscribers_options = this.params.see_subscribers_options || false; + this.thread = null; this.ds = new session.web.DataSet(this, this.view.model); this.ds_users = new session.web.DataSet(this, 'res.users'); }, start: function() { - this._super.apply(this, arguments); var self = this; - // bind buttons - this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); }).hide(); + this._super.apply(this, arguments); + mail.ChatterUtils.bind_events(this); + this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); }); + if (! this.params.see_subscribers_options) { + this.$element.find('button.oe_mail_button_followers').hide(); } this.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); }) .mouseover(function () { $(this).html('Follow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); }) .mouseleave(function () { $(this).html('Not following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); }); @@ -530,7 +865,8 @@ openerp.mail = function(session) { }, reinit: function() { - this.see_subscribers = true; + this.params.see_subscribers = true; + this.params.see_subscribers_options = this.params.see_subscribers_options || false; this.$element.find('button.oe_mail_button_followers').html('Hide followers') this.$element.find('button.oe_mail_button_follow').hide(); this.$element.find('button.oe_mail_button_unfollow').hide(); @@ -548,11 +884,11 @@ openerp.mail = function(session) { // fetch followers var fetch_sub_done = this.fetch_subscribers(); // create and render Thread widget - this.$element.find('div.oe_mail_recthread_left').empty(); + this.$element.find('div.oe_mail_recthread_main').empty(); if (this.thread) this.thread.destroy(); this.thread = new mail.Thread(this, {'res_model': this.view.model, 'res_id': this.view.datarecord.id, 'uid': this.session.uid, - 'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15}); - var thread_done = this.thread.appendTo(this.$element.find('div.oe_mail_recthread_left')); + 'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15}); + var thread_done = this.thread.appendTo(this.$element.find('div.oe_mail_recthread_main')); return fetch_sub_done && thread_done; }, @@ -567,9 +903,8 @@ openerp.mail = function(session) { this.$element.find('div.oe_mail_recthread_followers h4').html('Followers (' + records.length + ')'); _(records).each(function (record) { if (record.id == self.session.uid) { self.is_subscriber = true; } - var mini_url = self.thread_get_avatar('res.users', 'avatar', record.id); - $('<li><img class="oe_mail_oe_left oe_mail_msg_image" src="' + mini_url + '"/>' + - '<a href="#" class="oe_mail_internal_link" data-res-model="res.users" data-res-id="' + record.id + '">' + record.name + '</a></li>').appendTo(user_list); + record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.users', 'avatar', record.id); + $(session.web.qweb.render('mail.record_thread.subscriber', {'record': record})).appendTo(user_list); }); if (self.is_subscriber) { self.$element.find('button.oe_mail_button_follow').hide(); @@ -591,30 +926,35 @@ openerp.mail = function(session) { }, do_toggle_followers: function () { - this.see_subscribers = ! this.see_subscribers; - if (this.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); } - else { this.$element.find('button.oe_mail_button_followers').html('Display followers'); } + this.params.see_subscribers = ! this.params.see_subscribers; + if (this.params.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); } + else { this.$element.find('button.oe_mail_button_followers').html('Show followers'); } this.$element.find('div.oe_mail_recthread_followers').toggle(); }, - - thread_get_avatar: function(model, field, id) { - return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || ''); - }, }); - session.web.form.widgets.add( 'mail_thread', 'openerp.mail.RecordThread'); - /** WallView widget: a wall of messages */ - mail.WallView = session.web.Widget.extend({ - template: 'mail.Wall', + + /** + * ------------------------------------------------------------ + * WallView Widget + * ------------------------------------------------------------ + * + * This widget handles the display of the Chatter on the Wall. + */ + + /* Add WallView widget to registry */ + session.web.client_actions.add('mail.wall', 'session.mail.Wall'); + + /* WallView widget: a wall of messages */ + mail.Wall = session.web.Widget.extend({ + template: 'mail.wall', /** * @param {Object} parent parent * @param {Object} [params] * @param {Number} [params.limit=20] number of messages to show and fetch * @param {Number} [params.search_view_id=false] search view id for messages - * @var {Array} sorted_comments records sorted by res_model and res_id - * records.res_model = {res_ids} - * records.res_model.res_id = [records] + * @var {Array} comments_structure (see chatter utils) */ init: function (parent, params) { this._super(parent); @@ -637,26 +977,49 @@ openerp.mail = function(session) { start: function () { this._super.apply(this, arguments); - var self = this; this.display_current_user(); // add events this.add_event_handlers(); // load mail.message search view var search_view_ready = this.load_search_view(this.params.search_view_id, {}, false); + // load composition form + var compose_done = this.instantiate_composition_form(); // fetch first threads var comments_ready = this.init_and_fetch_comments(this.params.limit, 0); - return (search_view_ready && comments_ready); + return (search_view_ready && comments_ready && compose_done); }, - - stop: function () { + + /** + * Override-hack of do_action: automatically reload the chatter. + * Normally it should be called only when clicking on 'Post/Send' + * in the composition form. */ + do_action: function(action, on_close) { + this.init_and_fetch_comments(); + if (this.compose_message_widget) { + this.compose_message_widget.reinit(); } + return this._super(action, on_close); + }, + + destroy: function () { this._super.apply(this, arguments); }, - + + instantiate_composition_form: function(mode, msg_id) { + if (this.compose_message_widget) { + this.compose_message_widget.destroy(); + } + this.compose_message_widget = new mail.ComposeMessage(this, { + 'extended_mode': false, 'uid': this.session.uid, 'res_model': 'res.users', + 'res_id': this.session.uid, 'mode': mode || 'comment', 'msg_id': msg_id }); + var composition_node = this.$element.find('div.oe_mail_wall_action'); + composition_node.empty(); + var compose_done = this.compose_message_widget.appendTo(composition_node); + return compose_done; + }, + /** Add events */ add_event_handlers: function () { var self = this; - // post a comment - this.$element.find('.oe_mail_wall_action button').click(function () { return self.do_comment(); }); // display more threads this.$element.find('button.oe_mail_wall_button_more').click(function () { return self.do_more(); }); }, @@ -699,13 +1062,9 @@ openerp.mail = function(session) { }, display_current_user: function () { - return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid)); - }, - - thread_get_avatar: function(model, field, id) { - return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || ''); - }, - + //return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid)); + }, + /** * Initializes the wall and calls fetch_comments * @param {Number} limit: number of notifications to fetch @@ -735,8 +1094,8 @@ openerp.mail = function(session) { else var fetch_domain = this.search['domain']; if (additional_context) var fetch_context = _.extend(this.search['context'], additional_context); else var fetch_context = this.search['context']; - return this.ds_thread.call('get_pushed_messages', - [[this.session.uid], (limit || 0), (offset || 0), fetch_domain, true, [], fetch_context]).then(this.proxy('display_comments')); + return this.ds_thread.call('message_get_pushed_messages', + [[this.session.uid], true, [], (limit || 0), (offset || 0), fetch_domain, fetch_context]).then(this.proxy('display_comments')); }, /** @@ -745,16 +1104,16 @@ openerp.mail = function(session) { display_comments: function (records) { var self = this; this.do_update_show_more(records.length >= self.params.limit); - this.sort_comments(records); + mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, false); _(this.comments_structure['new_root_ids']).each(function (root_id) { var records = self.comments_structure.tree_struct[root_id]['for_thread_msgs']; var model_name = self.comments_structure.msgs[root_id]['model']; var res_id = self.comments_structure.msgs[root_id]['res_id']; - var render_res = session.web.qweb.render('mail.WallThreadContainer', {}); + var render_res = session.web.qweb.render('mail.wall_thread_container', {}); $('<li class="oe_mail_wall_thread">').html(render_res).appendTo(self.$element.find('ul.oe_mail_wall_threads')); var thread = new mail.Thread(self, { 'res_model': model_name, 'res_id': res_id, 'uid': self.session.uid, 'records': records, - 'parent_id': false, 'thread_level': self.params.thread_level, 'show_hide': true} + 'parent_id': false, 'thread_level': self.params.thread_level, 'show_hide': true, 'is_wall': true} ); self.thread_list.push(thread); return thread.appendTo(self.$element.find('li.oe_mail_wall_thread:last')); @@ -765,18 +1124,10 @@ openerp.mail = function(session) { }, /** - * Add records to comments_structure object: see function for details - */ - sort_comments: function(records) { - tools_sort_comments(this.comments_structure, records, false); - }, - - /** * Create a domain to fetch new comments according to - * comments already present in sorted_comments + * comments already present in comments_structure * - for each model: * -- should not be child of already displayed ids - * @param {Object} sorted_comments (see sort_comments) * @returns {Array} fetch_domain (OpenERP domain style) */ get_fetch_domain: function () { @@ -801,17 +1152,5 @@ openerp.mail = function(session) { var domain = this.get_fetch_domain(); return this.fetch_comments(this.params.limit, 0, domain); }, - - /** Action: Posts a comment */ - do_comment: function () { - var comment_node = this.$element.find('.oe_mail_wall_action textarea'); - var body_text = comment_node.val(); - comment_node.val(''); - var call_done = this.ds_users.call('message_append_note', [[this.session.uid], 'Tweet', body_text, false, 'comment', 'html']).then(this.proxy('init_and_fetch_comments')); - }, }); - session.web.client_actions.add('mail.all_feeds', 'session.mail.WallView'); - }; - -// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index ce15f83a3d4..bf8d7fd1475 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -1,7 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> <template> - <div t-name="mail.Wall" class="oe_view_manager oe_mail_wall oe_view_manager_current"> + <!-- + wall main template + Template used to display the communication history in the wall. + --> + <div t-name="mail.wall" class="oe_view_manager oe_mail_wall oe_view_manager_current"> <table class="oe_view_manager_header"> <colgroup> <col width="33%"/> @@ -21,30 +25,44 @@ </table> <div class="oe_mail_wall_main"> <div class="oe_mail_wall_action"> - <img class="oe_mail_msg_image oe_left" alt="User img"/> - <textarea class="oe_mail oe_mail_wall_action_textarea" placeholder="What are you working on?"/> - <button class="oe_right oe_mail_wall_button_comment" type="button">Post comment</button> + <!-- call the composition form --> + <t t-call ="mail.compose_message"/> </div> - <div class="oe_clear"></div> + <div class="oe_clear"/> <ul class="oe_mail_wall_threads"> + <!-- contains threads --> </ul> <div class="oe_mail_wall_more"> <button class="oe_mail_wall_button_more" type="button">See more discussions</button> </div> </div> - <div class="oe_mail_wall_aside"></div> + <div class="oe_mail_wall_aside"> + <!-- contains currently nothing --> + </div> </div> - <t t-name="mail.WallThreadContainer"> + <!-- + wall_thread_container template for the wall + Each discussion thread is contained inside this template + --> + <t t-name="mail.wall_thread_container"> </t> - <div t-name="mail.RecordThread" class="oe_mail_recthread"> - <div class="oe_mail_recthread_left"></div> - <div class="oe_mail_recthread_right"> + <!-- + record_thread main template + Template used to display the communication history in documents + form view. + --> + <div t-name="mail.record_thread" class="oe_mail_recthread"> + <!-- <h4>History and Comments</h4> --> + <div class="oe_mail_recthread_main"> + <!-- contains the document thread --> + </div> + <div class="oe_mail_recthread_aside"> <div class="oe_mail_recthread_actions"> <button type="button" class="oe_mail_button_follow oe_mail_button_mouseout">Not following</button> <button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button> - <button type="button" class="oe_mail_button_followers">Display followers</button> + <button type="button" class="oe_mail_button_followers">Show followers</button> </div> <div class="oe_mail_recthread_followers"> <h4>Followers</h4> @@ -53,44 +71,87 @@ </div> </div> - <div t-name="mail.Thread" class="oe_mail oe_mail_thread"> + <!-- + record_thread.subscriber template + Template used to display a subscriber. + --> + <li t-name="mail.record_thread.subscriber"> + <img class="oe_mail_thumbnail oe_mail_frame" t-attf-src="{record.avatar_url}"/> + <a t-attf-href="#model=res.users&id=#{record.id}"><t t-raw="record.name"/></a> + </li> + + <!-- + mail.compose_message template + This template holds the composition form to write a note or send + an e-mail. It contains by default a textarea, that will be replaced + by another composition form in the main wall composition form, or + for main thread composition form in document form view. + --> + <t t-name="mail.compose_message"> + <div> + <img class="oe_mail_icon oe_mail_frame oe_left" alt="User img"/> + <div class="oe_mail_msg_content"> + <!-- contains the composition form --> + <!-- default content: old basic textarea --> + <textarea class="oe_mail_compose_textarea" placeholder="Add your comment here..." onfocus="this.value = '';"/> + </div> + <div class="oe_clear"/> + </div> + </t> + + <!-- + thread template + This template holds a thread of comments. It begins with an actions + container, holding the composition form. Then come the various + messages. Then comes the 'more' button. + --> + <ul t-name="mail.thread" class="oe_mail oe_mail_thread"> <div class="oe_mail_thread_action"> - <img class="oe_mail_msg_image oe_left" alt="User img"/> - <textarea class="oe_mail oe_mail_action_textarea" placeholder="Add your comment here..." onfocus="this.value = '';"/> + <!-- contains the composition box (form + image) --> + <t t-call="mail.compose_message"/> + </div> + <div class="oe_mail_thread_display"> + <!-- contains the threads --> </div> - <div class="oe_mail_thread_display"></div> <div class="oe_mail_thread_more"> <button class="oe_mail_button_more" type="button">Load more messages</button> </div> - </div> + </ul> <!-- default layout --> - <div t-name="mail.Thread.message" class="oe_mail oe_mail_thread_msg"> + <li t-name="mail.thread.message" class="oe_mail oe_mail_thread_msg"> <div t-attf-class="oe_mail_msg_#{record.type}"> - <img class="oe_email_icon oe_left" t-att-src="record.mini_url"/> + <img class="oe_mail_icon oe_left" t-att-src="record.mini_url"/> <div class="oe_mail_msg_content"> + <!-- dropdown menu with message options and actions --> <span class="oe_dropdown_toggle oe_dropdown_arrow"> <ul class="oe_dropdown_menu"> - <t t-if="thread._is_author(record.user_id[0]) && display['show_delete']"> - <li><a href="#" t-attf-data-id='{record.id}' class="oe_mail_msg_delete">Delete</a></li> + <t t-if="display['show_delete']"> + <li t-if="record.is_author"><a href="#" class="oe_mail_msg_delete" t-attf-data-id='{record.id}'>Delete</a></li> </t> - <t t-if="!thread._is_author(record.user_id[0]) && display['show_hide']"> - <li><a href="#" t-attf-data-id='{record.id}' class="oe_mail_msg_hide">Hide</a></li> - </t> - <li t-if="record.type == 'email'"><a href="#" class="oe_mail_msg_details">Details</a></li> + <li t-if="display['show_hide']"><a href="#" class="oe_mail_msg_hide" t-attf-data-id='{record.id}'>Remove from Wall</a></li> +<!-- Uncomment when adding subtype hiding + <li t-if="display['show_hide']"> + <a href="#" class="oe_mail_msg_hide_type" t-attf-data-subtype='{record.subtype}'>Hide '<t t-esc="record.subtype"/>' for this document</a> + </li> --> + <li><a href="#" t-attf-data-msg_id="{record.id}" t-attf-data-type="{record.type}" t-attf-data-formatting="{record.content_subtype}" class="oe_mail_msg_reply_by_email">Reply by email</a></li> + <li t-if="record.type == 'email'"><a t-attf-href="#model=mail.message&id=#{record.id}" class="oe_mail_msg_details">Details</a></li> </ul> </span> + <!-- message itself --> <div class="oe_mail_msg"> - <h1 t-if="record.subject && record.subject != 'Reply'" class="oe_mail_msg_title"> + <h1 t-if="record.subject" class="oe_mail_msg_title"> <t t-raw="record.subject"/> </h1> <div t-if="params.thread_level > 0" class="oe_mail_msg_subtitle"> - <a href="#" t-attf-data-res-model='{params.res_model}' t-attf-data-res-id='{params.res_id}' class="oe_mail_internal_link"><t t-raw="record.record_name"/></a> + <a t-attf-href="#model=#{params.res_model}&id=#{params.res_id}"><t t-raw="record.record_name"/></a> </div> + <div class="oe_clear"/> <div class="oe_mail_msg_body"> - <a href="#" data-res-model='res.users' t-attf-data-res-id='{record.user_id[0]}' class="oe_mail_internal_link"><t t-raw="record.user_id[1]"/></a> - <t t-raw="record.body"/> + <a t-attf-href="#model=res.users&id=#{record.user_id[0]}"><t t-raw="record.user_id[1]"/></a> + <div class="oe_mail_msg_record_body"><t t-raw="record.body"/></div> </div> + <div class="oe_clear"/> <ul class="oe_mail_msg_footer"> <li><span t-att-title="record.date"><t t-raw="record.timerelative"/></span></li> <li t-if="display['show_reply']"><a href="#" class="oe_mail_msg_reply">Reply</a></li> @@ -115,5 +176,6 @@ </div> </div> </div> - </div> + </li> + </template> diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 1b19a383c10..d26511464f8 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -33,7 +33,7 @@ from ..mail_message import to_email # main mako-like expression pattern EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') -class mail_compose_message(osv.osv_memory): +class mail_compose_message(osv.TransientModel): """Generic Email composition wizard. This wizard is meant to be inherited at model and view level to provide specific wizard features. @@ -41,13 +41,12 @@ class mail_compose_message(osv.osv_memory): 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']``. + reply to a previous message mode and pre-populated with the original + quote. If set to 'comment', it means you are writing a new message to + be attached to a document. 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. * 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 @@ -61,119 +60,179 @@ class mail_compose_message(osv.osv_memory): _description = 'Email composition wizard' def default_get(self, cr, uid, fields, context=None): - """Overridden to provide specific defaults depending on the context - parameters. + """ Overridden to provide specific defaults depending on the context + parameters. + + Composition mode + - comment: default mode; active_model, active_id = model and ID of a + document we are commenting, + - reply: active_id = ID of a mail.message to which we are replying. + From this message we can find the related model and res_id, + - mass_mailing mode: active_model, active_id = model and ID of a + document we are commenting, :param dict context: several context values will modify the behavior of the wizard, cfr. the class description. """ if context is None: context = {} + compose_mode = context.get('mail.compose.message.mode', 'comment') + active_model = context.get('active_model') + active_id = context.get('active_id') result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) + + # get default values according to the composition mode 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 compose_mode in ['reply']: + vals = self.get_message_data(cr, uid, int(context['active_id']), context=context) + elif compose_mode in ['comment', 'mass_mail'] and active_model and active_id: + vals = self.get_value(cr, uid, active_model, active_id, context) for field in vals: if field in fields: - result.update({field : vals[field]}) + result[field] = vals[field] # 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 + if not result.get('model') and active_model: + result['model'] = active_model + if not result.get('res_id') and active_id: + result['res_id'] = active_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) + current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context) result['email_from'] = current_user.user_email or False return result _columns = { + 'dest_partner_ids': fields.many2many('res.partner', + 'email_message_send_partner_rel', + 'wizard_id', 'partner_id', 'Destination partners', + help="When sending emails through the social network composition wizard"\ + "you may choose to send a copy of the mail to partners."), '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. + """ 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. + """ + result = {} + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + result.update({ + 'model': model, + 'res_id': res_id, + 'email_from': user.user_email or tools.config.get('email_from', False), + 'body_html': False, + 'body_text': False, + 'subject': False, + 'dest_partner_ids': [], + }) + return result - :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. + def onchange_email_mode(self, cr, uid, ids, value, model, res_id, context=None): + """ email_mode (values: True or False). This onchange on the email mode + allows to have some specific behavior when going in email mode, or + when going out of email mode. + Basically, dest_partner_ids is reset when going out of email + mode. + This method can be overridden for models that want to have their + specific behavior. + Note that currently, this onchange is used in mail.js and called + manually on the form instantiated in the Chatter. """ - return {} + if not value: + return {'value': {'dest_partner_ids': []}} + return {'value': {}} + + def onchange_formatting(self, cr, uid, ids, value, model, res_id, context=None): + """ onchange_formatting (values: True or False). This onchange on the + formatting allows to have some specific behavior when going in + formatting mode, or when going out of formatting. + Basically, subject is reset when going out of formatting mode. + This method can be overridden for models that want to have their + specific behavior. + Note that currently, this onchange is used in mail.js and called + manually on the form instantiated in the Chatter. + """ + if not value: + return {'value': {'subject': False}} + return {'value': {}} 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'``. + """ 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). It 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. """ 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 or '' - 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 or '') - 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, - }) + if not message_id: + return result + + current_user = self.pool.get('res.users').browse(cr, uid, uid, context) + message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context) + # Form the subject + re_prefix = _("Re:") + reply_subject = tools.ustr(message_data.subject or '') + if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)): + reply_subject = "%s %s" % (re_prefix, reply_subject) + # Form the bodies (text and html). We use the plain text version of the + # original mail, by default, as it is easier to quote than the HTML + # version. TODO: make it possible to switch to HTML on the fly + 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'))} + body_text = message_data.body_text or '' + body_html = message_data.body_html or '' + quoted_body_text = '> %s' % tools.ustr(body_text.replace('\n', "\n> ") or '') + quoted_body_html = '<blockquote>%s</blockquote>' % (tools.ustr(body_html)), + reply_body_text = '\n%s%s\n%s\n%s' % (sent_date, sender, quoted_body_text, current_user.signature) + reply_body_html = '<br /><br />%s%s<br />%s<br />%s' % (sent_date, sender, quoted_body_html, current_user.signature) + # form dest_partner_ids + dest_partner_ids = [partner.id for partner in message_data.partner_ids] + # Update header and references + reply_headers = {} + reply_references = message_data.references and tools.ustr(message_data.references) or False + reply_message_id = message_data.message_id or False + if reply_message_id: + reply_references = (reply_references or '') + " " + message_data.message_id + reply_headers['In-Reply-To'] = message_data.message_id + # update the result + result.update({ + 'body_text': reply_body_text, + 'body_html': reply_body_html, + 'subject': reply_subject, + 'attachment_ids': [], + 'dest_partner_ids': dest_partner_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': reply_message_id, + 'references': reply_references, + 'headers': reply_headers, + }) return result def send_mail(self, cr, uid, ids, context=None): @@ -190,76 +249,81 @@ class mail_compose_message(osv.osv_memory): ''' if context is None: context = {} - mail_message = self.pool.get('mail.message') - for mail in self.browse(cr, uid, ids, context=context): + # composition wizard options + email_mode = context.get('email_mode') + formatting = context.get('formatting') + mass_mail_mode = context.get('mail.compose.message.mode') == 'mass_mail' + + mail_message_obj = self.pool.get('mail.message') + for mail_wiz in self.browse(cr, uid, ids, context=context): + # attachments attachment = {} - for attach in mail.attachment_ids: + for attach in mail_wiz.attachment_ids: attachment[attach.datas_fname] = attach.datas and attach.datas.decode('base64') + + # default values, according to the wizard options + subject = mail_wiz.subject if formatting else False + content_subtype = 'html' if formatting else 'plain' + type = 'email' if email_mode else 'comment' + state = 'outgoing' if email_mode else False + partner_ids = [partner.id for partner in mail_wiz.dest_partner_ids] references = None headers = {} - - body = mail.body_html if mail.subtype == 'html' else mail.body_text - - # Get model, and check whether it is OpenChatter enabled, aka inherit from mail.thread - if context.get('mail.compose.message.mode') == 'mass_mail': - 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, ast.literal_eval(mail.filter_id.domain), context=ast.literal_eval(mail.filter_id.context)) + body = mail_wiz.body_html if content_subtype == 'html' else mail_wiz.body_text + + # get model, active_ids, and check if model is openchatter-enabled + if mass_mail_mode and context.get('active_ids') and context.get('active_model'): + active_ids = context['active_ids'] + active_model = context['active_model'] + elif mass_mail_mode: + active_model = mail_wiz.model + active_model_pool = self.pool.get(active_model) + active_ids = active_model_pool.search(cr, uid, ast.literal_eval(mail_wiz.filter_id.domain), context=ast.literal_eval(mail_wiz.filter_id.context)) else: - active_model = mail.model - active_ids = [int(mail.res_id)] + active_model = mail_wiz.model + active_ids = [mail_wiz.res_id] active_model_pool = self.pool.get(active_model) - if hasattr(active_model_pool, '_inherit') and 'mail.thread' in active_model_pool._inherit: - mail_thread_enabled = True - else: - mail_thread_enabled = False - - # 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 + mail_thread_enabled = hasattr(active_model_pool, 'message_append') if context.get('mail.compose.message.mode') == 'mass_mail': # Mass mailing: must render the template patterns 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) - + rendered_subject = self.render_template(cr, uid, subject, active_model, active_id) + rendered_body_html = self.render_template(cr, uid, mail_wiz.body_html, active_model, active_id) + rendered_body_text = self.render_template(cr, uid, mail_wiz.body_text, active_model, active_id) + email_from = self.render_template(cr, uid, mail_wiz.email_from, active_model, active_id) + email_to = self.render_template(cr, uid, mail_wiz.email_to, active_model, active_id) + email_cc = self.render_template(cr, uid, mail_wiz.email_cc, active_model, active_id) + email_bcc = self.render_template(cr, uid, mail_wiz.email_bcc, active_model, active_id) + reply_to = self.render_template(cr, uid, mail_wiz.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. if mail_thread_enabled: - active_model_pool.message_append(cr, uid, [active_id], - subject, body_text=mail.body_text, body_html=mail.body_html, subtype=mail.subtype, state='outgoing', - email_to=email_to, email_from=email_from, email_cc=email_cc, email_bcc=email_bcc, + active_model_pool.message_append(cr, uid, [active_id], rendered_subject, rendered_body_text, rendered_body_html, + type=type, content_subtype=content_subtype, state=state, partner_ids=partner_ids, + email_from=email_from, email_to=email_to, email_cc=email_cc, email_bcc=email_bcc, reply_to=reply_to, references=references, attachments=attachment, headers=headers, context=context) else: - 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=active_id, - subtype=mail.subtype, headers=headers, context=context) + mail_message_obj.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, rendered_body_text, + model=mail_wiz.model, email_cc=to_email(email_cc), email_bcc=to_email(email_bcc), reply_to=reply_to, + attachments=attachment, references=references, res_id=active_id, partner_ids=partner_ids, + content_subtype=mail_wiz.content_subtype, headers=headers, context=context) else: # normal mode - no mass-mailing if mail_thread_enabled: - msg_ids = active_model_pool.message_append(cr, uid, active_ids, - mail.subject, body_text=mail.body_text, body_html=mail.body_html, subtype=mail.subtype, state='outgoing', - email_to=mail.email_to, email_from=mail.email_from, email_cc=mail.email_cc, email_bcc=mail.email_bcc, - reply_to=mail.reply_to, references=references, attachments=attachment, headers=headers, context=context) + msg_ids = active_model_pool.message_append(cr, uid, active_ids, subject, mail_wiz.body_text, mail_wiz.body_html, + type=type, content_subtype=content_subtype, state=state, partner_ids=partner_ids, + email_from=mail_wiz.email_from, email_to=mail_wiz.email_to, email_cc=mail_wiz.email_cc, email_bcc=mail_wiz.email_bcc, + reply_to=mail_wiz.reply_to, references=references, attachments=attachment, headers=headers, context=context) else: - msg_ids = [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)] + msg_ids = [mail_message_obj.schedule_with_attach(cr, uid, mail_wiz.email_from, to_email(mail_wiz.email_to), subject, mail_wiz.body_text, + type=type, model=mail_wiz.model, email_cc=to_email(mail_wiz.email_cc), email_bcc=to_email(mail_wiz.email_bcc), reply_to=mail_wiz.reply_to, + attachments=attachment, references=references, res_id=int(mail_wiz.res_id), partner_ids=partner_ids, + content_subtype=mail_wiz.content_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_ids, context=context) + if type == 'email': + mail_message_obj.send(cr, uid, msg_ids, context=context) return {'type': 'ir.actions.act_window_close'} @@ -293,8 +357,7 @@ class mail_compose_message(osv.osv_memory): return template and EXPRESSION_PATTERN.sub(merge, template) - -class mail_compose_message_extended(osv.osv_memory): +class mail_compose_message_extended(osv.TransientModel): """ Extension of 'mail.compose.message' to support default field values related to CRM-like models that follow the following conventions: @@ -309,26 +372,64 @@ class mail_compose_message_extended(osv.osv_memory): _inherit = 'mail.compose.message' def get_value(self, cr, uid, model, res_id, context=None): - """Overrides the default implementation to provide more default field values - related to the corresponding CRM case. + """ Overrides the default implementation to provide more default field values + related to the corresponding CRM case. """ - result = super(mail_compose_message_extended, self).get_value(cr, uid, model, res_id, context=context) + result = super(mail_compose_message_extended, self).get_value(cr, uid, model, res_id, context=context) model_obj = self.pool.get(model) if getattr(model_obj, '_mail_compose_message', False) and res_id: data = model_obj.browse(cr, uid , res_id, context) - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) result.update({ - 'model': model, - 'res_id': res_id, - 'email_from': user.user_email or tools.config.get('email_from', False), 'email_to': data.email_from or False, 'email_cc': tools.ustr(data.email_cc or ''), 'subject': data.name or False, - 'body_text': '\n' + tools.ustr(user.signature or ''), - 'subtype': 'plain', }) if hasattr(data, 'section_id'): result['reply_to'] = data.section_id and data.section_id.reply_to or False return result + def onchange_email_mode(self, cr, uid, ids, value, model, res_id, context=None): + """ Overrides the default implementation to provide default values for + dest_partner_ids. This method checks that a partner maching the + ``email_from`` of the record exists. It it does not exist, it + creates a new partner. The found or created partner is then added + in dest_partner_ids. + Partner check/creation valid inly if the value is True, and if + the model has the ``_mail_compose_message`` attribute. + """ + result = super(mail_compose_message_extended, self).onchange_email_mode(cr, uid, ids, value, model, res_id, context=context) + model_obj = self.pool.get(model) + if not value or not (getattr(model_obj, '_mail_compose_message', False) and res_id): + return result + data = model_obj.browse(cr, uid , res_id, context=context) + partner_obj = self.pool.get('res.partner') + partner_ids = partner_obj.search(cr, uid, [('email', '=', data.email_from)], context=context) + if partner_ids: + partner_id = partner_ids[0] + else: + partner_id = partner_obj.name_create(cr, uid, data.email_from, context=context)[0] + result['value'].update({ + 'dest_partner_ids': [partner_id], + 'email_cc': tools.ustr(data.email_cc or ''), + }) + if hasattr(data, 'section_id'): + result['value']['reply_to'] = data.section_id and data.section_id.reply_to or False + return result + + def onchange_formatting(self, cr, uid, ids, value, model, res_id, context=None): + """ Overrides the default implementation to provide default values for + the subject. + Subject re-creation valid only if the value is True, and if the + model has the ``_mail_compose_message`` attribute. + """ + result = super(mail_compose_message_extended, self).onchange_formatting(cr, uid, ids, value, model, res_id, context=context) + model_obj = self.pool.get(model) + if not value or not (getattr(model_obj, '_mail_compose_message', False) and res_id): + return result + data = model_obj.browse(cr, uid , res_id, context=context) + result['value'].update({ + 'subject': data.name or False, + }) + return result + # 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 index bcc11c4111d..8fa4eaa9c50 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -37,6 +37,50 @@ </field> </record> + <record model="ir.ui.view" id="email_compose_message_wizard_form_chatter"> + <field name="name">mail.compose.message.form</field> + <field name="model">mail.compose.message</field> + <field name="priority">18</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Compose Email" version="7.0" > + <group> + <field name="subject" colspan="2" nolabel="1" placeholder="Subject..." + class="oe_mail_compose_message_subject oe_mail_compose_message_invisible"/> + <field name="body_text" colspan="2" nolabel="1" placeholder="What are you working on ?" + class="oe_mail_compose_message_body_text"/> + <field name="body_html" colspan="2" nolabel="1" placeholder="What are you working on HTML ?" + class="oe_mail_compose_message_body_html oe_mail_compose_message_invisible" widget="text_html"/> + <field name="dest_partner_ids" colspan="2" nolabel="1" widget="many2many_tags" + placeholder="Add contacts to notify..." + class="oe_mail_compose_message_partner_ids oe_mail_compose_message_invisible"/> + <div> + <button name="send_mail" string="Post" type="object" + class="oe_mail_compose_message_button_send"/> + or <a href="#" class="oe_mail_compose_message_email">Send an Email</a> + </div> + <div class='oe_mail_compose_message_icons'> +<!-- <button icon="../../../../../mail/static/src/img/checklist" + type="object" name="checklist" string="" + help="Add a checklist"/> --> + <a href="#" class="oe_mail_compose_message_checklist"> + <img src='/mail/static/src/img/checklist.png/' alt='Checklist' + title='Add a checklist'/> + </a> + <a href="#" class="oe_mail_compose_message_attachment"> + <img src='/mail/static/src/img/attachment.png/' alt='Attachment' + title='Add an attachment'/> + </a> + <a href="#" class="oe_mail_compose_message_formatting"> + <img src='/mail/static/src/img/formatting.png/' alt='Formatting' + title='Switch to advanced formatting mode'/> + </a> + </div> + </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> diff --git a/addons/mrp/__openerp__.py b/addons/mrp/__openerp__.py index bdf6f63b755..d1f5625e09f 100644 --- a/addons/mrp/__openerp__.py +++ b/addons/mrp/__openerp__.py @@ -27,6 +27,7 @@ "website" : "http://www.openerp.com", "category" : "Manufacturing", "sequence": 18, + "summary": "Manufacturing, Production, Bill of Materials", "images" : ["images/bill_of_materials.jpeg", "images/manufacturing_order.jpeg", "images/planning_manufacturing_order.jpeg", "images/production_analysis.jpeg", "images/production_dashboard.jpeg","images/routings.jpeg","images/work_centers.jpeg"], "depends" : ["procurement", "stock", "resource", "purchase", "product","process"], "description": """ diff --git a/addons/mrp/i18n/mn.po b/addons/mrp/i18n/mn.po new file mode 100644 index 00000000000..7c97b68cc46 --- /dev/null +++ b/addons/mrp/i18n/mn.po @@ -0,0 +1,2231 @@ +# Mongolian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:49+0000\n" +"PO-Revision-Date: 2012-07-21 12:33+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Mongolian <mn@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-07-22 04:45+0000\n" +"X-Generator: Launchpad (build 15654)\n" + +#. module: mrp +#: view:mrp.routing.workcenter:0 +msgid "Routing Work Centers" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.mrp_production_action +msgid "" +"Manufacturing Orders are usually proposed automatically by OpenERP based on " +"the bill of materials and the procurement rules, but you can also create " +"manufacturing orders manually. OpenERP will handle the consumption of the " +"raw materials (stock decrease) and the production of the finished products " +"(stock increase) when the order is processed." +msgstr "" + +#. module: mrp +#: help:mrp.production,location_src_id:0 +msgid "Location where the system will look for components." +msgstr "" + +#. module: mrp +#: field:mrp.production,workcenter_lines:0 +msgid "Work Centers Utilisation" +msgstr "" + +#. module: mrp +#: model:product.template,name:mrp.product_sugar_product_template +msgid "Sugar" +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "No. Of Cycles" +msgstr "" + +#. module: mrp +#: field:mrp.routing.workcenter,cycle_nbr:0 +msgid "Number of Cycles" +msgstr "" + +#. module: mrp +#: model:product.uom.categ,name:mrp.product_uom_categ_fluid +msgid "Fluid" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_minimumstockprocure0 +msgid "" +"The 'Minimum stock rule' allows the system to create procurement orders " +"automatically as soon as the minimum stock is reached." +msgstr "" + +#. module: mrp +#: field:mrp.production,picking_id:0 +msgid "Picking list" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:130 +#, python-format +msgid "Hourly Cost" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:139 +#, python-format +msgid "Cost Price per Uom" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Scrap Products" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.mrp_routing_action +#: model:ir.ui.menu,name:mrp.menu_mrp_routing_action +msgid "Routings" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,product_id:0 +msgid "Work Center Product" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +msgid "Search Bill Of Material" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_stockproduct1 +msgid "For stockable products and consumables" +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_stockproduction0 +msgid "To Produce" +msgstr "" + +#. module: mrp +#: help:mrp.routing.workcenter,cycle_nbr:0 +msgid "" +"Number of iterations this work center has to do in the specified operation " +"of the routing." +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +#: field:mrp.bom,code:0 +#: view:mrp.production:0 +#: field:mrp.production,name:0 +msgid "Reference" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Finished Products" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Manufacturing Orders which are currently in production." +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_servicerfq0 +#: model:process.transition,name:mrp.process_transition_stockrfq0 +msgid "To Buy" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_purchaseprocure0 +msgid "The system launches automatically a RFQ to the preferred supplier." +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Products to Finish" +msgstr "" + +#. module: mrp +#: selection:mrp.bom,method:0 +msgid "Set / Pack" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +#: field:mrp.production,state:0 +msgid "State" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,costs_hour:0 +msgid "Cost per hour" +msgstr "" + +#. module: mrp +#: model:product.template,name:mrp.product_orange_product_template +msgid "Orange" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_servicemts0 +msgid "" +"This is used in case of a service without any impact in the system, a " +"training session for instance." +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "UOM" +msgstr "" + +#. module: mrp +#: field:change.production.qty,product_qty:0 +#: field:mrp.bom,product_qty:0 +#: field:mrp.production,product_qty:0 +#: field:mrp.production.product.line,product_qty:0 +msgid "Product Qty" +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,product_id:0 +msgid "" +"Fill this product to track easily your production costs in the analytic " +"accounting." +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_purchaseprocure0 +msgid "For purchased material" +msgstr "" + +#. module: mrp +#: field:mrp.bom.revision,indice:0 +msgid "Revision" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.product_form_config_action +msgid "" +"Create a product form for everything you buy or sell. Specify a supplier if " +"the product can be purchased." +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.next_id_77 +msgid "Reporting" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,costs_cycle_account_id:0 +msgid "Cycle Account" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:130 +#, python-format +msgid "Work Cost" +msgstr "" + +#. module: mrp +#: report:bom.structure:0 +msgid "[" +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_procureserviceproduct0 +msgid "Procurement of services" +msgstr "" + +#. module: mrp +#: view:mrp.workcenter:0 +msgid "Capacity Information" +msgstr "" + +#. module: mrp +#: field:mrp.production,move_created_ids2:0 +msgid "Produced Products" +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "Destination Location" +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menu_mrp_bom +msgid "Master Data" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_stockmts0 +msgid "" +"The system waits for the products to be available in the stock. These " +"products are typically procured manually or through a minimum stock rule." +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "Partner Ref" +msgstr "" + +#. module: mrp +#: field:mrp.production,origin:0 +#: report:mrp.production.order:0 +msgid "Source Document" +msgstr "" + +#. module: mrp +#: field:mrp.production,product_lines:0 +msgid "Scheduled goods" +msgstr "" + +#. module: mrp +#: selection:mrp.bom,type:0 +msgid "Sets / Phantom" +msgstr "" + +#. module: mrp +#: help:mrp.bom,position:0 +msgid "Reference to a position in an external plan." +msgstr "" + +#. module: mrp +#: constraint:stock.move:0 +msgid "You try to assign a lot which is not from the same product" +msgstr "" + +#. module: mrp +#: model:product.template,name:mrp.product_cloth_product_template +msgid "Cloth" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_product_produce +msgid "Product Produce" +msgstr "" + +#. module: mrp +#: constraint:mrp.bom:0 +msgid "Error ! You cannot create recursive BoM." +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_routing_workcenter +msgid "Work Center Usage" +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_procurestockableproduct0 +msgid "Procurement of stockable Product" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +msgid "Default UOM" +msgstr "" + +#. module: mrp +#: sql_constraint:mrp.production:0 +#: sql_constraint:stock.picking:0 +msgid "Reference must be unique per Company!" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:139 +#: report:bom.structure:0 +#: field:mrp.product_price,number:0 +#: report:mrp.production.order:0 +#, python-format +msgid "Quantity" +msgstr "" + +#. module: mrp +#: field:mrp.production.workcenter.line,hour:0 +msgid "Nbr of hours" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Confirm Production" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_stockproduct0 +msgid "" +"The system creates an order (production or purchased) depending on the sold " +"quantity and the products parameters." +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_stockproduction0 +msgid "" +"In case the Supply method of the product is Produce, the system creates a " +"production order." +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.mrp_property_action +msgid "" +"The Properties in OpenERP are used to select the right bill of materials for " +"manufacturing a product when you have different ways of building the same " +"product. You can assign several properties to each Bill of Materials. When a " +"sales person creates a sales order, he can relate it to several properties " +"and OpenERP will automatically select the BoM to use according the needs." +msgstr "" + +#. module: mrp +#: help:mrp.production,picking_id:0 +msgid "" +"This is the Internal Picking List that brings the finished product to the " +"production plan" +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menu_view_resource_calendar_search_mrp +msgid "Working Time" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.action_report_in_out_picking_tree +msgid "Weekly Stock Value Variation" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +#: field:mrp.production,date_planned_date:0 +#: report:mrp.production.order:0 +msgid "Scheduled Date" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +msgid "Component Product" +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "Bill Of Material" +msgstr "" + +#. module: mrp +#: help:mrp.routing,location_id:0 +msgid "" +"Keep empty if you produce at the location where the finished products are " +"needed.Set a location if you produce at a fixed location. This can be a " +"partner location if you subcontract the manufacturing operations." +msgstr "" + +#. module: mrp +#: view:board.board:0 +msgid "Stock Value Variation" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.action2 +msgid "Bill of Materials Structure" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_serviceproduct0 +msgid "Product type is service" +msgstr "" + +#. module: mrp +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.mrp_property_group_action +msgid "" +"Define specific property groups that can be assigned to the properties of " +"your bill of materials." +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,costs_cycle:0 +msgid "Specify Cost of Work Center per cycle." +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_bom0 +msgid "Manufacturing decomposition" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_serviceproduct1 +msgid "For Services." +msgstr "" + +#. module: mrp +#: field:mrp.bom.revision,date:0 +msgid "Modification Date" +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,costs_cycle_account_id:0 +#: help:mrp.workcenter,costs_hour_account_id:0 +msgid "" +"Complete this only if you want automatic analytic accounting entries on " +"production orders." +msgstr "" + +#. module: mrp +#: field:mrp.production.workcenter.line,cycle:0 +msgid "Nbr of cycles" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_orderrfq0 +#: model:process.node,note:mrp.process_node_rfq0 +msgid "Request for Quotation." +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_billofmaterialrouting0 +msgid "" +"The Bill of Material is linked to a routing, i.e. the succession of work " +"centers." +msgstr "" + +#. module: mrp +#: constraint:product.product:0 +msgid "Error: Invalid ean code" +msgstr "" + +#. module: mrp +#: field:mrp.production,move_created_ids:0 +msgid "Products to Produce" +msgstr "" + +#. module: mrp +#: view:mrp.routing:0 +#: field:mrp.routing,location_id:0 +msgid "Production Location" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Change Qty" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.action_configure_workcenter +msgid "Configure your work centers" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Force Reservation" +msgstr "" + +#. module: mrp +#: field:mrp.bom.revision,author_id:0 +msgid "Author" +msgstr "" + +#. module: mrp +#: field:report.mrp.inout,value:0 +msgid "Stock value" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.action_product_bom_structure +msgid "Product BoM Structure" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Search Production" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:139 +#, python-format +msgid "Supplier Price per Uom" +msgstr "" + +#. module: mrp +#: help:mrp.routing.workcenter,sequence:0 +msgid "" +"Gives the sequence order when displaying a list of routing Work Centers." +msgstr "" + +#. module: mrp +#: constraint:stock.move:0 +msgid "You can not move products from or to a location of the type view." +msgstr "" + +#. module: mrp +#: field:mrp.bom,child_complete_ids:0 +msgid "BoM Hierarchy" +msgstr "" + +#. module: mrp +#: field:mrp.bom,product_uom:0 +#: field:mrp.production,product_uom:0 +#: field:mrp.production.product.line,product_uom:0 +msgid "Product UOM" +msgstr "" + +#. module: mrp +#: selection:mrp.production,state:0 +msgid "Picking Exception" +msgstr "" + +#. module: mrp +#: field:mrp.bom,bom_lines:0 +msgid "BoM Lines" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,time_start:0 +msgid "Time before prod." +msgstr "" + +#. module: mrp +#: help:mrp.routing,active:0 +msgid "" +"If the active field is set to False, it will allow you to hide the routing " +"without removing it." +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_billofmaterialrouting0 +msgid "Material Routing" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +#: field:mrp.production,move_lines2:0 +#: report:mrp.production.order:0 +msgid "Consumed Products" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.action_mrp_workcenter_load_wizard +#: model:ir.model,name:mrp.model_mrp_workcenter_load +#: model:ir.model,name:mrp.model_report_workcenter_load +msgid "Work Center Load" +msgstr "" + +#. module: mrp +#: code:addons/mrp/procurement.py:43 +#, python-format +msgid "No BoM defined for this product !" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.mrp_bom_form_action2 +#: model:ir.ui.menu,name:mrp.menu_mrp_bom_form_action2 +msgid "Bill of Material Components" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menu_mrp_planning +msgid "Planning" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Ready" +msgstr "" + +#. module: mrp +#: model:product.template,name:mrp.product_buttons_product_template +msgid "Shirt Buttons" +msgstr "" + +#. module: mrp +#: help:mrp.production,routing_id:0 +msgid "" +"The list of operations (list of work centers) to produce the finished " +"product. The routing is mainly used to compute work center costs during " +"operations and to plan future loads on work centers based on production " +"plannification." +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,time_cycle:0 +msgid "Time in hours for doing one cycle." +msgstr "" + +#. module: mrp +#: constraint:mrp.bom:0 +msgid "BoM line product should not be same as BoM product." +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "In Production" +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menu_mrp_property +msgid "Master Bill of Materials" +msgstr "" + +#. module: mrp +#: help:mrp.bom,product_uos:0 +msgid "" +"Product UOS (Unit of Sale) is the unit of measurement for the invoicing and " +"promotion of stock." +msgstr "" + +#. module: mrp +#: view:mrp.product_price:0 +#: view:mrp.workcenter.load:0 +msgid "Print" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +#: view:mrp.workcenter:0 +msgid "Type" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_minimumstockrule0 +msgid "Linked to the 'Minimum stock rule' supplying method." +msgstr "" + +#. module: mrp +#: selection:mrp.workcenter.load,time_unit:0 +msgid "Per month" +msgstr "" + +#. module: mrp +#: code:addons/mrp/wizard/change_production_qty.py:78 +#: code:addons/mrp/wizard/change_production_qty.py:83 +#, python-format +msgid "Couldn't find bill of material for product" +msgstr "" + +#. module: mrp +#: report:bom.structure:0 +msgid "Product Name" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:503 +#, python-format +msgid "Invalid action !" +msgstr "" + +#. module: mrp +#: help:mrp.bom,product_efficiency:0 +msgid "A factor of 0.9 means a loss of 10% within the production process." +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:734 +#: code:addons/mrp/mrp.py:762 +#, python-format +msgid "Warning!" +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "Printing date" +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_orderrfq0 +#: model:process.node,name:mrp.process_node_rfq0 +msgid "RFQ" +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_producttostockrules0 +msgid "Procurement rule" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Partial" +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "WorkCenter" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_procureserviceproduct0 +msgid "" +"Depending on the chosen method to 'supply' the service, the procurement " +"order creates a RFQ for a subcontracting purchase order or waits until the " +"service is done (= the delivery of the products)." +msgstr "" + +#. module: mrp +#: selection:mrp.production,priority:0 +msgid "Urgent" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Manufacturing Orders which are waiting for raw materials." +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.mrp_workcenter_action +msgid "" +"Work Centers allow you to create and manage manufacturing units. They " +"consist of workers and/or machines, which are considered as units for " +"capacity and planning forecast. Keep in mind that the working time and " +"resource leave are not taken into account in the time computation of the " +"work center." +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_production +msgid "Manufacturing Order" +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_productionprocureproducts0 +msgid "Procurement of raw material" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +#: field:mrp.production,cycle_total:0 +msgid "Total Cycles" +msgstr "" + +#. module: mrp +#: selection:mrp.production,state:0 +msgid "Ready to Produce" +msgstr "" + +#. module: mrp +#: field:mrp.bom.revision,name:0 +msgid "Modification name" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +#: view:mrp.production:0 +msgid "Date" +msgstr "" + +#. module: mrp +#: field:mrp.bom,type:0 +msgid "BoM Type" +msgstr "" + +#. module: mrp +#: code:addons/mrp/procurement.py:45 +#, python-format +msgid "" +"Procurement '%s' has an exception: 'No BoM defined for this product !'" +msgstr "" + +#. module: mrp +#: view:mrp.property:0 +msgid "Search" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:626 +#, python-format +msgid "Could not cancel manufacturing order !" +msgstr "" + +#. module: mrp +#: field:report.workcenter.load,cycle:0 +msgid "Nbr of cycle" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_res_company +msgid "Companies" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:627 +#, python-format +msgid "" +"You must first cancel related internal picking attached to this " +"manufacturing order." +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_minimumstockrule0 +#: model:process.node,name:mrp.process_node_productminimumstockrule0 +msgid "Minimum Stock" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:503 +#, python-format +msgid "Cannot delete a manufacturing order in state '%s'" +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menus_dash_mrp +msgid "Dashboard" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:160 +#: code:addons/mrp/report/price.py:211 +#, python-format +msgid "Total Cost of %s %s" +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_stockproduct0 +#: model:process.node,name:mrp.process_node_stockproduct1 +#: model:process.process,name:mrp.process_process_stockableproductprocess0 +msgid "Stockable Product" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:130 +#, python-format +msgid "Work Center name" +msgstr "" + +#. module: mrp +#: field:mrp.routing,code:0 +msgid "Code" +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "No. Of Hours" +msgstr "" + +#. module: mrp +#: view:mrp.property:0 +#: view:mrp.property.group:0 +msgid "Property Group" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Qty" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_production0 +msgid "Manufacturing Plan." +msgstr "" + +#. module: mrp +#: view:mrp.routing:0 +#: view:mrp.workcenter:0 +msgid "Inactive" +msgstr "" + +#. module: mrp +#: view:change.production.qty:0 +#: view:mrp.product.produce:0 +#: view:mrp.product_price:0 +#: view:mrp.production:0 +#: view:mrp.workcenter.load:0 +msgid "Cancel" +msgstr "" + +#. module: mrp +#: code:addons/mrp/wizard/change_production_qty.py:63 +#, python-format +msgid "Active Id is not found" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_servicerfq0 +msgid "" +"If the service has a 'Buy' supply method, this creates a RFQ, a " +"subcontracting demand for instance." +msgstr "" + +#. module: mrp +#: field:mrp.production,move_prod_id:0 +msgid "Move product" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Late" +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_servicemts0 +msgid "Make to stock" +msgstr "" + +#. module: mrp +#: report:bom.structure:0 +msgid "BOM Name" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Start Production" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.open_board_manufacturing +#: model:ir.ui.menu,name:mrp.menu_board_manufacturing +msgid "Production Dashboard" +msgstr "" + +#. module: mrp +#: model:res.groups,name:mrp.group_mrp_manager +msgid "Manager" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Source Loc." +msgstr "" + +#. module: mrp +#: field:mrp.bom,position:0 +msgid "Internal Reference" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_billofmaterial0 +msgid "Product's structure" +msgstr "" + +#. module: mrp +#: field:mrp.bom,name:0 +#: report:mrp.production.order:0 +#: field:mrp.production.product.line,name:0 +#: field:mrp.routing,name:0 +#: field:mrp.routing.workcenter,name:0 +msgid "Name" +msgstr "" + +#. module: mrp +#: field:mrp.product.produce,mode:0 +msgid "Mode" +msgstr "" + +#. module: mrp +#: report:bom.structure:0 +msgid "]" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter.load,measure_unit:0 +msgid "Amount measuring unit" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.mrp_production_action_planning +msgid "" +"Manufacturing Orders describe the operations that need to be carried out and " +"the raw materials usage for each production stage. You use specifications " +"(bills of materials or BoM) to work out the raw material requirements and " +"the manufacturing orders needed for the finished products. Once the bills of " +"materials have been defined, OpenERP is capable of automatically deciding on " +"the manufacturing route depending on the needs of the company." +msgstr "" + +#. module: mrp +#: model:ir.actions.todo.category,name:mrp.category_mrp_config +msgid "MRP Management" +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,costs_hour:0 +msgid "Specify Cost of Work Center per hour." +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,capacity_per_cycle:0 +msgid "" +"Number of operations this Work Center can do in parallel. If this Work " +"Center represents a team of 5 workers, the capacity per cycle is 5." +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.mrp_production_action3 +msgid "Manufacturing Orders in Progress" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +#: view:mrp.production:0 +#: view:mrp.property:0 +#: view:mrp.routing:0 +#: view:mrp.workcenter:0 +msgid "Group By..." +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:130 +#, python-format +msgid "Cycles Cost" +msgstr "" + +#. module: mrp +#: selection:mrp.workcenter.load,measure_unit:0 +msgid "Amount in cycles" +msgstr "" + +#. module: mrp +#: field:mrp.production,location_dest_id:0 +msgid "Finished Products Location" +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menu_pm_resources_config +msgid "Resources" +msgstr "" + +#. module: mrp +#: help:mrp.routing.workcenter,hour_nbr:0 +msgid "" +"Time in hours for this Work Center to achieve the operation of the specified " +"routing." +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,costs_journal_id:0 +msgid "Analytic Journal" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.mrp_workcenter_action +#: model:ir.ui.menu,name:mrp.menu_view_resource_search_mrp +#: field:mrp.routing,workcenter_lines:0 +msgid "Work Centers" +msgstr "" + +#. module: mrp +#: selection:mrp.workcenter.load,time_unit:0 +msgid "Per week" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.mrp_routing_action +msgid "" +"Routings allow you to create and manage the manufacturing operations that " +"should be followed within your work centers in order to produce a product. " +"They are attached to bills of materials that will define the required raw " +"materials." +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.product_form_config_action +msgid "Create or Import Products" +msgstr "" + +#. module: mrp +#: field:report.workcenter.load,hour:0 +msgid "Nbr of hour" +msgstr "" + +#. module: mrp +#: view:mrp.routing:0 +msgid "Work Center Operations" +msgstr "" + +#. module: mrp +#: view:mrp.routing:0 +msgid "Notes" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Manufacturing Orders which are ready to start production." +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_bom +#: view:mrp.bom:0 +#: field:mrp.production,bom_id:0 +#: model:process.node,name:mrp.process_node_billofmaterial0 +msgid "Bill of Material" +msgstr "" + +#. module: mrp +#: view:mrp.workcenter.load:0 +msgid "Select time unit" +msgstr "" + +#. module: mrp +#: view:report.workcenter.load:0 +msgid "Work Center load" +msgstr "" + +#. module: mrp +#: help:mrp.production,location_dest_id:0 +msgid "Location where the system will stock the finished products." +msgstr "" + +#. module: mrp +#: help:mrp.routing.workcenter,routing_id:0 +msgid "" +"Routing indicates all the Work Centers used, for how long and/or cycles.If " +"Routing is indicated then,the third tab of a production order (Work Centers) " +"will be automatically pre-completed." +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_producttostockrules0 +msgid "" +"The Minimum Stock Rule is an automatic procurement rule based on a mini and " +"maxi quantity. It's available in the Inventory management menu and " +"configured by product." +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:187 +#, python-format +msgid "Components Cost of %s %s" +msgstr "" + +#. module: mrp +#: selection:mrp.workcenter.load,time_unit:0 +msgid "Day by day" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +msgid "Revisions" +msgstr "" + +#. module: mrp +#: model:product.template,name:mrp.product_shirt_product_template +msgid "Shirt" +msgstr "" + +#. module: mrp +#: field:mrp.production,priority:0 +msgid "Priority" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_stock_picking +msgid "Picking List" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:1000 +#, python-format +msgid "Manufacturing order '%s' is scheduled for the %s." +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "Production Order N° :" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:647 +#, python-format +msgid "Manufacturing order '%s' is ready to produce." +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_production_product_line +msgid "Production Scheduled Product" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:204 +#, python-format +msgid "Work Cost of %s %s" +msgstr "" + +#. module: mrp +#: help:res.company,manufacturing_lead:0 +msgid "Security days for each manufacturing operation." +msgstr "" + +#. module: mrp +#: model:product.template,name:mrp.product_water_product_template +msgid "Water" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +msgid "Component Name" +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_mts0 +#: model:process.transition,name:mrp.process_transition_servicemts0 +#: model:process.transition,name:mrp.process_transition_stockmts0 +msgid "Make to Stock" +msgstr "" + +#. module: mrp +#: constraint:mrp.production:0 +msgid "Order quantity cannot be negative or zero!" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.mrp_bom_form_action +msgid "" +"Master Bills of Materials allow you to create and manage the list of " +"necessary raw materials used to make a finished product. OpenERP will use " +"these BoMs to automatically propose manufacturing orders according to " +"product needs. You can either create a bill of materials to define specific " +"production steps or define a single multi-level bill of materials." +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_stockrfq0 +msgid "" +"In case the Supply method of the product is Buy, the system creates a " +"purchase order." +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_procurement_order +msgid "Procurement" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.action_view_mrp_product_price_wizard +#: view:mrp.product_price:0 +msgid "Product Cost Structure" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:139 +#, python-format +msgid "Components suppliers" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Production Work Centers" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Split in production lots" +msgstr "" + +#. module: mrp +#: view:mrp.workcenter:0 +msgid "Search for mrp workcenter" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +msgid "BoM Structure" +msgstr "" + +#. module: mrp +#: field:mrp.production,date_start:0 +msgid "Start Date" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,costs_hour_account_id:0 +msgid "Hour Account" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Destination Loc." +msgstr "" + +#. module: mrp +#: field:mrp.bom,method:0 +msgid "Method" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Pending" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:603 +#, python-format +msgid "Couldn't find a bill of material for this product." +msgstr "" + +#. module: mrp +#: field:mrp.bom,active:0 +#: field:mrp.routing,active:0 +msgid "Active" +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_procureproducts0 +msgid "Procure Products" +msgstr "" + +#. module: mrp +#: view:report.workcenter.load:0 +msgid "Work Center Loads" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_bom_revision +msgid "Bill of Material Revision" +msgstr "" + +#. module: mrp +#: help:mrp.production,origin:0 +msgid "" +"Reference of the document that generated this production order request." +msgstr "" + +#. module: mrp +#: sql_constraint:mrp.bom:0 +msgid "" +"All product quantities must be greater than 0.\n" +"You should install the mrp_subproduct module if you want to manage extra " +"products on BoMs !" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Extra Information" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_change_production_qty +msgid "Change Quantity of Products" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_productionorder0 +msgid "Drives the procurement orders for raw material." +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,costs_general_account_id:0 +msgid "General Account" +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "SO Number" +msgstr "" + +#. module: mrp +#: selection:mrp.production,state:0 +msgid "Done" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.mrp_production_action4 +msgid "Manufacturing Orders Waiting Products" +msgstr "" + +#. module: mrp +#: selection:mrp.production,priority:0 +msgid "Not urgent" +msgstr "" + +#. module: mrp +#: field:mrp.production,user_id:0 +msgid "Responsible" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.mrp_production_action2 +msgid "Manufacturing Orders To Start" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_workcenter +#: field:mrp.production.workcenter.line,workcenter_id:0 +#: field:mrp.routing.workcenter,workcenter_id:0 +#: view:mrp.workcenter:0 +#: field:report.workcenter.load,workcenter_id:0 +msgid "Work Center" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,capacity_per_cycle:0 +msgid "Capacity per Cycle" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_product_product +#: view:mrp.bom:0 +#: field:mrp.bom,product_id:0 +#: view:mrp.production:0 +#: field:mrp.production,product_id:0 +#: report:mrp.production.order:0 +#: field:mrp.production.product.line,product_id:0 +msgid "Product" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +#: field:mrp.production,hour_total:0 +msgid "Total Hours" +msgstr "" + +#. module: mrp +#: field:mrp.production,location_src_id:0 +msgid "Raw Materials Location" +msgstr "" + +#. module: mrp +#: view:mrp.product_price:0 +msgid "Print Cost Structure of Product." +msgstr "" + +#. module: mrp +#: field:mrp.bom,product_uos:0 +#: field:mrp.production.product.line,product_uos:0 +msgid "Product UOS" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Consume Products" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.act_mrp_product_produce +#: view:mrp.product.produce:0 +#: view:mrp.production:0 +msgid "Produce" +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_stock0 +#: model:process.transition,name:mrp.process_transition_servicemto0 +#: model:process.transition,name:mrp.process_transition_stockproduct0 +msgid "Make to Order" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:358 +#, python-format +msgid "Copy" +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,note:0 +msgid "" +"Description of the Work Center. Explain here what's a cycle according to " +"this Work Center." +msgstr "" + +#. module: mrp +#: view:mrp.production.lot.line:0 +msgid "Production Products" +msgstr "" + +#. module: mrp +#: field:mrp.production,date_finished:0 +msgid "End Date" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,resource_id:0 +msgid "Resource" +msgstr "" + +#. module: mrp +#: help:mrp.bom,date_start:0 +#: help:mrp.bom,date_stop:0 +msgid "Validity of this BoM or component. Keep empty if it's always valid." +msgstr "" + +#. module: mrp +#: field:mrp.production,product_uos:0 +msgid "Product UoS" +msgstr "" + +#. module: mrp +#: selection:mrp.production,priority:0 +msgid "Very Urgent" +msgstr "" + +#. module: mrp +#: help:mrp.bom,routing_id:0 +msgid "" +"The list of operations (list of work centers) to produce the finished " +"product. The routing is mainly used to compute work center costs during " +"operations and to plan future loads on work centers based on production " +"planning." +msgstr "" + +#. module: mrp +#: view:change.production.qty:0 +msgid "Approve" +msgstr "" + +#. module: mrp +#: view:mrp.property.group:0 +msgid "Properties categories" +msgstr "" + +#. module: mrp +#: help:mrp.production.workcenter.line,sequence:0 +msgid "Gives the sequence order when displaying a list of work orders." +msgstr "" + +#. module: mrp +#: report:mrp.production.order:0 +msgid "Source Location" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +#: view:mrp.production.product.line:0 +msgid "Scheduled Products" +msgstr "" + +#. module: mrp +#: view:mrp.production.lot.line:0 +msgid "Production Products Consommation" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.act_product_manufacturing_open +#: model:ir.actions.act_window,name:mrp.mrp_production_action +#: model:ir.actions.act_window,name:mrp.mrp_production_action_planning +#: model:ir.ui.menu,name:mrp.menu_mrp_production_action +#: model:ir.ui.menu,name:mrp.menu_mrp_production_order_action +#: view:mrp.production:0 +msgid "Manufacturing Orders" +msgstr "" + +#. module: mrp +#: help:mrp.product.produce,mode:0 +msgid "" +"'Consume only' mode will only consume the products with the quantity " +"selected.\n" +"'Consume & Produce' mode will consume as well as produce the products with " +"the quantity selected and it will finish the production order when total " +"ordered quantities are produced." +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +#: report:mrp.production.order:0 +msgid "Work Orders" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,costs_cycle:0 +msgid "Cost per cycle" +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_serviceproduct0 +#: model:process.node,name:mrp.process_node_serviceproduct1 +msgid "Service" +msgstr "" + +#. module: mrp +#: selection:mrp.production,state:0 +msgid "Cancelled" +msgstr "" + +#. module: mrp +#: help:mrp.bom,product_uom:0 +msgid "" +"UoM (Unit of Measure) is the unit of measurement for the inventory control" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:734 +#, python-format +msgid "" +"You are going to consume total %s quantities of \"%s\".\n" +"But you can only consume up to total %s quantities." +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_bom0 +msgid "" +"The Bill of Material is the product's decomposition. The components (that " +"are products themselves) can also have their own Bill of Material (multi-" +"level)." +msgstr "" + +#. module: mrp +#: field:mrp.bom,company_id:0 +#: field:mrp.production,company_id:0 +#: field:mrp.routing,company_id:0 +#: field:mrp.routing.workcenter,company_id:0 +#: view:mrp.workcenter:0 +msgid "Company" +msgstr "" + +#. module: mrp +#: selection:mrp.workcenter.load,measure_unit:0 +msgid "Amount in hours" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,time_cycle:0 +msgid "Time for 1 cycle (hour)" +msgstr "" + +#. module: mrp +#: model:ir.actions.report.xml,name:mrp.report_mrp_production_report +#: field:mrp.production.product.line,production_id:0 +#: field:mrp.production.workcenter.line,production_id:0 +#: model:process.node,name:mrp.process_node_production0 +#: model:process.node,name:mrp.process_node_productionorder0 +msgid "Production Order" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_productminimumstockrule0 +msgid "Automatic procurement rule" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Compute Data" +msgstr "" + +#. module: mrp +#: field:mrp.production,product_uos_qty:0 +msgid "Product UoS Qty" +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:139 +#: view:mrp.bom:0 +#, python-format +msgid "Components" +msgstr "" + +#. module: mrp +#: report:bom.structure:0 +#: model:ir.actions.report.xml,name:mrp.report_bom_structure +msgid "BOM Structure" +msgstr "" + +#. module: mrp +#: field:mrp.bom,date_stop:0 +msgid "Valid Until" +msgstr "" + +#. module: mrp +#: field:mrp.bom,date_start:0 +msgid "Valid From" +msgstr "" + +#. module: mrp +#: selection:mrp.bom,type:0 +msgid "Normal BoM" +msgstr "" + +#. module: mrp +#: field:res.company,manufacturing_lead:0 +msgid "Manufacturing Lead Time" +msgstr "" + +#. module: mrp +#: field:mrp.bom,product_uos_qty:0 +#: field:mrp.production.product.line,product_uos_qty:0 +msgid "Product UOS Qty" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.action_report_in_out_picking_tree +msgid "" +"Weekly Stock Value Variation enables you to track the stock value evolution " +"linked to manufacturing activities, receptions of products and delivery " +"orders." +msgstr "" + +#. module: mrp +#: view:mrp.product.produce:0 +msgid "Confirm" +msgstr "" + +#. module: mrp +#: field:mrp.bom,product_efficiency:0 +msgid "Manufacturing Efficiency" +msgstr "" + +#. module: mrp +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "" + +#. module: mrp +#: help:mrp.bom,active:0 +msgid "" +"If the active field is set to False, it will allow you to hide the bills of " +"material without removing it." +msgstr "" + +#. module: mrp +#: field:mrp.bom,product_rounding:0 +msgid "Product Rounding" +msgstr "" + +#. module: mrp +#: selection:mrp.production,state:0 +msgid "New" +msgstr "" + +#. module: mrp +#: selection:mrp.product.produce,mode:0 +msgid "Consume Only" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Recreate Picking" +msgstr "" + +#. module: mrp +#: help:mrp.bom,type:0 +msgid "" +"If a sub-product is used in several products, it can be useful to create its " +"own BoM. Though if you don't want separated production orders for this sub-" +"product, select Set/Phantom as BoM type. If a Phantom BoM is used for a root " +"product, it will be sold and shipped as a set of components, instead of " +"being produced." +msgstr "" + +#. module: mrp +#: help:mrp.production,state:0 +msgid "" +"When the production order is created the state is set to 'Draft'.\n" +" If the order is confirmed the state is set to 'Waiting Goods'.\n" +" If any exceptions are there, the state is set to 'Picking Exception'. " +" \n" +"If the stock is available then the state is set to 'Ready to Produce'.\n" +" When the production gets started then the state is set to 'In Production'.\n" +" When the production is over, the state is set to 'Done'." +msgstr "" + +#. module: mrp +#: selection:mrp.bom,method:0 +msgid "On Order" +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menu_mrp_configuration +#: view:res.company:0 +msgid "Configuration" +msgstr "" + +#. module: mrp +#: view:mrp.bom:0 +msgid "Starting Date" +msgstr "" + +#. module: mrp +#: field:mrp.workcenter,time_stop:0 +msgid "Time after prod." +msgstr "" + +#. module: mrp +#: field:mrp.workcenter.load,time_unit:0 +msgid "Type of period" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Total Qty" +msgstr "" + +#. module: mrp +#: field:mrp.routing.workcenter,hour_nbr:0 +msgid "Number of Hours" +msgstr "" + +#. module: mrp +#: view:mrp.workcenter:0 +msgid "Costing Information" +msgstr "" + +#. module: mrp +#: model:process.node,name:mrp.process_node_purchaseprocure0 +msgid "Procurement Orders" +msgstr "" + +#. module: mrp +#: help:mrp.bom,product_rounding:0 +msgid "Rounding applied on the product quantity." +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_stock0 +msgid "Assignment from Production or Purchase Order." +msgstr "" + +#. module: mrp +#: field:mrp.routing.workcenter,routing_id:0 +msgid "Parent Routing" +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,time_start:0 +msgid "Time in hours for the setup." +msgstr "" + +#. module: mrp +#: model:product.template,name:mrp.product_orangejuice_product_template +msgid "Orange Juice" +msgstr "" + +#. module: mrp +#: field:mrp.bom.revision,bom_id:0 +#: field:procurement.order,bom_id:0 +msgid "BoM" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_report_mrp_inout +#: view:report.mrp.inout:0 +msgid "Stock value variation" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_mts0 +#: model:process.node,note:mrp.process_node_servicemts0 +msgid "Assignment from stock." +msgstr "" + +#. module: mrp +#: selection:mrp.production,state:0 +msgid "Waiting Goods" +msgstr "" + +#. module: mrp +#: field:mrp.bom.revision,last_indice:0 +msgid "last indice" +msgstr "" + +#. module: mrp +#: field:mrp.bom,revision_ids:0 +#: view:mrp.bom.revision:0 +msgid "BoM Revisions" +msgstr "" + +#. module: mrp +#: field:report.mrp.inout,date:0 +#: field:report.workcenter.load,name:0 +msgid "Week" +msgstr "" + +#. module: mrp +#: selection:mrp.production,priority:0 +msgid "Normal" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Production started late" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_routing0 +msgid "Manufacturing Steps." +msgstr "" + +#. module: mrp +#: code:addons/mrp/report/price.py:146 +#: model:ir.actions.report.xml,name:mrp.report_cost_structure +#, python-format +msgid "Cost Structure" +msgstr "" + +#. module: mrp +#: model:res.groups,name:mrp.group_mrp_user +msgid "User" +msgstr "" + +#. module: mrp +#: selection:mrp.product.produce,mode:0 +msgid "Consume & Produce" +msgstr "" + +#. module: mrp +#: field:mrp.bom,bom_id:0 +msgid "Parent BoM" +msgstr "" + +#. module: mrp +#: report:bom.structure:0 +msgid "BOM Ref" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,help:mrp.mrp_bom_form_action2 +msgid "" +"Bills of materials components are components and sub-products used to create " +"master bills of materials. Use this menu to search in which BoM a specific " +"component is used." +msgstr "" + +#. module: mrp +#: model:product.uom,name:mrp.product_uom_litre +msgid "Litre" +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:762 +#, python-format +msgid "" +"You are going to produce total %s quantities of \"%s\".\n" +"But you can only produce up to total %s quantities." +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_stockproduct0 +msgid "Product type is Stockable or Consumable." +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:603 +#: code:addons/mrp/wizard/change_production_qty.py:78 +#: code:addons/mrp/wizard/change_production_qty.py:83 +#, python-format +msgid "Error" +msgstr "" + +#. module: mrp +#: selection:mrp.production,state:0 +msgid "Production Started" +msgstr "" + +#. module: mrp +#: field:mrp.product.produce,product_qty:0 +msgid "Select Quantity" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.act_product_product_2_mrp_bom +#: model:ir.actions.act_window,name:mrp.mrp_bom_form_action +#: model:ir.ui.menu,name:mrp.menu_mrp_bom_form_action +#: field:product.product,bom_ids:0 +msgid "Bill of Materials" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.mrp_property_action +#: model:ir.ui.menu,name:mrp.menu_mrp_property_action +#: view:mrp.bom:0 +#: field:mrp.bom,property_ids:0 +#: view:mrp.property:0 +#: view:procurement.order:0 +#: field:procurement.order,property_ids:0 +msgid "Properties" +msgstr "" + +#. module: mrp +#: view:mrp.routing.workcenter:0 +#: view:mrp.workcenter:0 +msgid "General Information" +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +msgid "Productions" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_routing +#: view:mrp.bom:0 +#: field:mrp.bom,routing_id:0 +#: view:mrp.production:0 +#: field:mrp.production,routing_id:0 +#: view:mrp.routing:0 +#: model:process.node,name:mrp.process_node_routing0 +msgid "Routing" +msgstr "" + +#. module: mrp +#: field:mrp.production,date_planned:0 +msgid "Scheduled date" +msgstr "" + +#. module: mrp +#: constraint:stock.move:0 +msgid "You must assign a production lot for this product" +msgstr "" + +#. module: mrp +#: field:stock.move,production_id:0 +msgid "Production" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_production_workcenter_line +#: field:mrp.production.workcenter.line,name:0 +msgid "Work Order" +msgstr "" + +#. module: mrp +#: view:board.board:0 +msgid "Procurements in Exception" +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_minimumstockprocure0 +msgid "'Minimum stock rule' material" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_mrp_product_price +msgid "Product Price" +msgstr "" + +#. module: mrp +#: model:ir.model,name:mrp.model_stock_move_split +msgid "Split in Production lots" +msgstr "" + +#. module: mrp +#: view:change.production.qty:0 +msgid "Change Quantity" +msgstr "" + +#. module: mrp +#: view:change.production.qty:0 +#: model:ir.actions.act_window,name:mrp.action_change_production_qty +msgid "Change Product Qty" +msgstr "" + +#. module: mrp +#: view:mrp.bom.revision:0 +#: field:mrp.bom.revision,description:0 +#: view:mrp.property:0 +#: view:mrp.property.group:0 +#: field:mrp.routing,note:0 +#: view:mrp.routing.workcenter:0 +#: field:mrp.routing.workcenter,note:0 +#: view:mrp.workcenter:0 +#: field:mrp.workcenter,note:0 +msgid "Description" +msgstr "" + +#. module: mrp +#: view:board.board:0 +msgid "Manufacturing board" +msgstr "" + +#. module: mrp +#: field:mrp.production,date_planned_end:0 +msgid "Scheduled End Date" +msgstr "" + +#. module: mrp +#: model:process.node,note:mrp.process_node_procureproducts0 +msgid "The way to procurement depends on the product type." +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menu_mrp_manufacturing +msgid "Manufacturing" +msgstr "" + +#. module: mrp +#: view:board.board:0 +msgid "Next Production Orders" +msgstr "" + +#. module: mrp +#: model:ir.actions.act_window,name:mrp.mrp_property_group_action +#: model:ir.ui.menu,name:mrp.menu_mrp_property_group_action +msgid "Property Groups" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_procurestockableproduct0 +msgid "" +"Depending on the chosen method to supply the stockable products, the " +"procurement order creates a RFQ, a production order, ... " +msgstr "" + +#. module: mrp +#: code:addons/mrp/mrp.py:874 +#: code:addons/mrp/mrp.py:929 +#: code:addons/mrp/mrp.py:954 +#, python-format +msgid "PROD: %s" +msgstr "" + +#. module: mrp +#: help:mrp.workcenter,time_stop:0 +msgid "Time in hours for the cleaning." +msgstr "" + +#. module: mrp +#: model:process.transition,name:mrp.process_transition_purchaseprocure0 +msgid "Automatic RFQ" +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_servicemto0 +msgid "" +"If the service has a 'Produce' supply method, this creates a task in the " +"project management module of OpenERP." +msgstr "" + +#. module: mrp +#: model:process.transition,note:mrp.process_transition_productionprocureproducts0 +msgid "" +"In order to supply raw material (to be purchased or produced), the " +"production order creates as much procurement orders as components listed in " +"the BOM, through a run of the schedulers (MRP)." +msgstr "" + +#. module: mrp +#: help:mrp.product_price,number:0 +msgid "" +"Specify quantity of products to produce or buy. Report of Cost structure " +"will be displayed base on this quantity." +msgstr "" + +#. module: mrp +#: selection:mrp.bom,method:0 +msgid "On Stock" +msgstr "" + +#. module: mrp +#: field:mrp.bom,sequence:0 +#: report:mrp.production.order:0 +#: field:mrp.production.workcenter.line,sequence:0 +#: field:mrp.routing.workcenter,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: mrp +#: model:ir.ui.menu,name:mrp.menu_view_resource_calendar_leaves_search_mrp +msgid "Resource Leaves" +msgstr "" + +#. module: mrp +#: help:mrp.bom,sequence:0 +msgid "Gives the sequence order when displaying a list of bills of material." +msgstr "" + +#. module: mrp +#: view:mrp.production:0 +#: field:mrp.production,move_lines:0 +#: report:mrp.production.order:0 +msgid "Products to Consume" +msgstr "" diff --git a/addons/mrp_repair/i18n/ar.po b/addons/mrp_repair/i18n/ar.po index 35ac83fec75..ddc13ca9742 100644 --- a/addons/mrp_repair/i18n/ar.po +++ b/addons/mrp_repair/i18n/ar.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -184,8 +184,8 @@ msgstr "تحذير !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "ضريبة القيمة المضافة" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -803,6 +803,9 @@ msgstr "جاهز للتصليح" msgid "No partner !" msgstr "لا يوجد شريك !" +#~ msgid "VAT" +#~ msgstr "ضريبة القيمة المضافة" + #~ msgid "Do you really want to create the invoice(s) ?" #~ msgstr "هل تريد فعلاً إنشاء الفاتورة/الفواتبر؟" diff --git a/addons/mrp_repair/i18n/bg.po b/addons/mrp_repair/i18n/bg.po index 08191fcf27d..75c633410ca 100644 --- a/addons/mrp_repair/i18n/bg.po +++ b/addons/mrp_repair/i18n/bg.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,8 +169,8 @@ msgstr "Предупреждение!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "ДДС" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -785,5 +785,8 @@ msgstr "Няма контрагент!" #~ "Името на обекта трябва да започва с \"x_\" и да не съдържа никакви специални " #~ "символи!" +#~ msgid "VAT" +#~ msgstr "ДДС" + #~ msgid "Do you really want to create the invoice(s) ?" #~ msgstr "Искате ли да създадете фактурата/ите" diff --git a/addons/mrp_repair/i18n/bs.po b/addons/mrp_repair/i18n/bs.po index 526d3c41d1c..413d19342b0 100644 --- a/addons/mrp_repair/i18n/bs.po +++ b/addons/mrp_repair/i18n/bs.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/ca.po b/addons/mrp_repair/i18n/ca.po index f298ca712d8..7d0c984c5ac 100644 --- a/addons/mrp_repair/i18n/ca.po +++ b/addons/mrp_repair/i18n/ca.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -193,8 +193,8 @@ msgstr "Avís!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "CIF/NIF" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -842,6 +842,9 @@ msgstr "No existeix empresa!" #~ msgid "Repair Order Ref" #~ msgstr "Referència comanda reparació" +#~ msgid "VAT" +#~ msgstr "CIF/NIF" + #~ msgid "Repairs in quotation" #~ msgstr "Reparacions en pressupost" diff --git a/addons/mrp_repair/i18n/cs.po b/addons/mrp_repair/i18n/cs.po index 0c8bd6f3af8..ddfe269417d 100644 --- a/addons/mrp_repair/i18n/cs.po +++ b/addons/mrp_repair/i18n/cs.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/da.po b/addons/mrp_repair/i18n/da.po index 2f74c448a7a..fc36b6c5917 100644 --- a/addons/mrp_repair/i18n/da.po +++ b/addons/mrp_repair/i18n/da.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -170,7 +170,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/de.po b/addons/mrp_repair/i18n/de.po index c559b16d6f5..36647265cf2 100644 --- a/addons/mrp_repair/i18n/de.po +++ b/addons/mrp_repair/i18n/de.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -191,8 +191,8 @@ msgstr "Warnung !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "USt." +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -926,6 +926,9 @@ msgstr "Kein Partner" #~ msgid "Do you really want to create the invoice(s) ?" #~ msgstr "Wollen Sie wirklich die Rechnung(en) erstellen ?" +#~ msgid "VAT" +#~ msgstr "USt." + #~ msgid "" #~ "\n" #~ " The aim is to have a complete module to manage all products " diff --git a/addons/mrp_repair/i18n/es.po b/addons/mrp_repair/i18n/es.po index f0b5d2205cb..d44970c0c80 100644 --- a/addons/mrp_repair/i18n/es.po +++ b/addons/mrp_repair/i18n/es.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -191,8 +191,8 @@ msgstr "¡Aviso!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "CIF/NIF" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -870,6 +870,9 @@ msgstr "¡No existe empresa!" #~ msgid "Make invoices" #~ msgstr "Realizar facturas" +#~ msgid "VAT" +#~ msgstr "CIF/NIF" + #, python-format #~ msgid "You have to select a partner in the repair form !" #~ msgstr "¡Debe seleccionar una empresa en el formulario de reparación!" diff --git a/addons/mrp_repair/i18n/es_AR.po b/addons/mrp_repair/i18n/es_AR.po index 40858dd108c..368ffcbd7d5 100644 --- a/addons/mrp_repair/i18n/es_AR.po +++ b/addons/mrp_repair/i18n/es_AR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,8 +169,8 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "CUIT" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -865,6 +865,9 @@ msgstr "Sin Partner !" #~ msgid "Repair State" #~ msgstr "Reparar Estado" +#~ msgid "VAT" +#~ msgstr "CUIT" + #~ msgid "Repair Ref" #~ msgstr "Ref de Reparación" diff --git a/addons/mrp_repair/i18n/es_CR.po b/addons/mrp_repair/i18n/es_CR.po index e5cd95f091f..09f38dcda23 100644 --- a/addons/mrp_repair/i18n/es_CR.po +++ b/addons/mrp_repair/i18n/es_CR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" "Language: \n" #. module: mrp_repair @@ -192,8 +192,8 @@ msgstr "¡Aviso!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "CIF/NIF" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -823,6 +823,9 @@ msgstr "Listo para reparar" msgid "No partner !" msgstr "¡No existe empresa!" +#~ msgid "VAT" +#~ msgstr "CIF/NIF" + #~ msgid "Do you really want to create the invoice(s) ?" #~ msgstr "¿Desea crear la(s) factura(s)?" diff --git a/addons/mrp_repair/i18n/es_EC.po b/addons/mrp_repair/i18n/es_EC.po index 3c9cf62af31..ded01d3dcfc 100644 --- a/addons/mrp_repair/i18n/es_EC.po +++ b/addons/mrp_repair/i18n/es_EC.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,8 +169,8 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "CIF/NIF" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -790,6 +790,9 @@ msgstr "" #~ msgid "Repair State" #~ msgstr "Estado de reparación" +#~ msgid "VAT" +#~ msgstr "CIF/NIF" + #~ msgid "Repairs in quotation" #~ msgstr "Reparaciones en presupuesto" diff --git a/addons/mrp_repair/i18n/et.po b/addons/mrp_repair/i18n/et.po index ec12e85f5bd..4917c9c1a57 100644 --- a/addons/mrp_repair/i18n/et.po +++ b/addons/mrp_repair/i18n/et.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -174,8 +174,8 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "KM" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -883,3 +883,6 @@ msgstr "Partner puudub!" #~ msgid "Repairs to be invoiced" #~ msgstr "Remondid arveldamiseks" + +#~ msgid "VAT" +#~ msgstr "KM" diff --git a/addons/mrp_repair/i18n/fi.po b/addons/mrp_repair/i18n/fi.po index ad73e5efe54..17352e0098b 100644 --- a/addons/mrp_repair/i18n/fi.po +++ b/addons/mrp_repair/i18n/fi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -174,8 +174,8 @@ msgstr "Varoitus !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "ALV" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -877,6 +877,9 @@ msgstr "Ei kumppania!" #~ msgid "Quality" #~ msgstr "Laatu" +#~ msgid "VAT" +#~ msgstr "ALV" + #~ msgid "Make invoices" #~ msgstr "Luo laskut" diff --git a/addons/mrp_repair/i18n/fr.po b/addons/mrp_repair/i18n/fr.po index 37f960e2e4e..f6b929a2c88 100644 --- a/addons/mrp_repair/i18n/fr.po +++ b/addons/mrp_repair/i18n/fr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -191,8 +191,8 @@ msgstr "Avertissement !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "TVA" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -821,6 +821,9 @@ msgstr "Prêt à Réparer" msgid "No partner !" msgstr "Pas de partenaire !" +#~ msgid "VAT" +#~ msgstr "TVA" + #~ msgid "Repairs" #~ msgstr "Réparations" diff --git a/addons/mrp_repair/i18n/hi.po b/addons/mrp_repair/i18n/hi.po index ed579111c0d..3aab3ba0656 100644 --- a/addons/mrp_repair/i18n/hi.po +++ b/addons/mrp_repair/i18n/hi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -170,8 +170,8 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "वैट" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -783,6 +783,9 @@ msgstr "" #~ msgid "Repair Order Ref" #~ msgstr "मरम्मत के आदेश संदर्भ" +#~ msgid "VAT" +#~ msgstr "वैट" + #~ msgid "Repair Fees line" #~ msgstr "मरम्मत शुल्क लाइन" diff --git a/addons/mrp_repair/i18n/hr.po b/addons/mrp_repair/i18n/hr.po index 1c9ee5684f9..f7870013e05 100644 --- a/addons/mrp_repair/i18n/hr.po +++ b/addons/mrp_repair/i18n/hr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -185,8 +185,8 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "PDV" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -806,5 +806,8 @@ msgstr "Spremno za popravak" msgid "No partner !" msgstr "" +#~ msgid "VAT" +#~ msgstr "PDV" + #~ msgid "Do you really want to create the invoice(s) ?" #~ msgstr "Kreirati račun(e) ?" diff --git a/addons/mrp_repair/i18n/hu.po b/addons/mrp_repair/i18n/hu.po index 140027c8895..eed7cc7f539 100644 --- a/addons/mrp_repair/i18n/hu.po +++ b/addons/mrp_repair/i18n/hu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -170,8 +170,8 @@ msgstr "Vigyázat!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "ÁFA" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -777,5 +777,8 @@ msgstr "" msgid "No partner !" msgstr "Nincs partner!" +#~ msgid "VAT" +#~ msgstr "ÁFA" + #~ msgid "Do you really want to create the invoice(s) ?" #~ msgstr "Valóban el akarja készíteni a számlá(ka)t?" diff --git a/addons/mrp_repair/i18n/id.po b/addons/mrp_repair/i18n/id.po index 73d46eefe95..c5a715bf5b9 100644 --- a/addons/mrp_repair/i18n/id.po +++ b/addons/mrp_repair/i18n/id.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/it.po b/addons/mrp_repair/i18n/it.po index ac804044667..a93f02cac15 100644 --- a/addons/mrp_repair/i18n/it.po +++ b/addons/mrp_repair/i18n/it.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,8 +169,8 @@ msgstr "Attenzione!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "IVA" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -784,6 +784,9 @@ msgstr "Nessun partner!" #~ msgid "Repair Order Ref" #~ msgstr "Rif. Ordine riparazione" +#~ msgid "VAT" +#~ msgstr "IVA" + #~ msgid "Invalid XML for View Architecture!" #~ msgstr "XML non valido per Visualizzazione Architettura!" diff --git a/addons/mrp_repair/i18n/ja.po b/addons/mrp_repair/i18n/ja.po index 338d7de68db..2b28d8f1bba 100644 --- a/addons/mrp_repair/i18n/ja.po +++ b/addons/mrp_repair/i18n/ja.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -180,8 +180,8 @@ msgstr "警告" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "消費税" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -793,3 +793,6 @@ msgstr "修理準備完了" #, python-format msgid "No partner !" msgstr "パートナがありません。" + +#~ msgid "VAT" +#~ msgstr "消費税" diff --git a/addons/mrp_repair/i18n/ko.po b/addons/mrp_repair/i18n/ko.po index dd1dc6dca74..51a13009fbc 100644 --- a/addons/mrp_repair/i18n/ko.po +++ b/addons/mrp_repair/i18n/ko.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -170,7 +170,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/lt.po b/addons/mrp_repair/i18n/lt.po index 7b661b27a5e..b3cf1cb3461 100644 --- a/addons/mrp_repair/i18n/lt.po +++ b/addons/mrp_repair/i18n/lt.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/mrp_repair.pot b/addons/mrp_repair/i18n/mrp_repair.pot index 884f6a30d00..3f21da6f93d 100644 --- a/addons/mrp_repair/i18n/mrp_repair.pot +++ b/addons/mrp_repair/i18n/mrp_repair.pot @@ -154,7 +154,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/nl.po b/addons/mrp_repair/i18n/nl.po index f7543992873..82518015dea 100644 --- a/addons/mrp_repair/i18n/nl.po +++ b/addons/mrp_repair/i18n/nl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -191,8 +191,8 @@ msgstr "Waarschuwing !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "BTW" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -758,7 +758,7 @@ msgstr "Verwijder" #: field:mrp.repair.fee,product_uom:0 #: field:mrp.repair.line,product_uom:0 msgid "Product UoM" -msgstr "Produkt UoM" +msgstr "Product maateenheid" #. module: mrp_repair #: field:mrp.repair,partner_invoice_id:0 @@ -818,6 +818,9 @@ msgstr "Gereed voor reparatie" msgid "No partner !" msgstr "Geen relatie !" +#~ msgid "VAT" +#~ msgstr "BTW" + #~ msgid "Invalid XML for View Architecture!" #~ msgstr "Ongeldige XML voor overzicht" diff --git a/addons/mrp_repair/i18n/nl_BE.po b/addons/mrp_repair/i18n/nl_BE.po index 6c424a6abd1..0f680be7664 100644 --- a/addons/mrp_repair/i18n/nl_BE.po +++ b/addons/mrp_repair/i18n/nl_BE.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/pl.po b/addons/mrp_repair/i18n/pl.po index 5055793e582..036178a1f9b 100644 --- a/addons/mrp_repair/i18n/pl.po +++ b/addons/mrp_repair/i18n/pl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,8 +169,8 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "VAT" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -788,6 +788,9 @@ msgstr "Brak partnera !" #~ msgid "Repair State" #~ msgstr "Stan naprawy" +#~ msgid "VAT" +#~ msgstr "VAT" + #~ msgid "Repairs in quotation" #~ msgstr "Naprawy w ofercie" diff --git a/addons/mrp_repair/i18n/pt.po b/addons/mrp_repair/i18n/pt.po index f10ee11ad74..acaab4321d6 100644 --- a/addons/mrp_repair/i18n/pt.po +++ b/addons/mrp_repair/i18n/pt.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -190,8 +190,8 @@ msgstr "Atenção!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "IVA" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -828,6 +828,9 @@ msgstr "Nenhum Parceiro !" #~ msgid "Make invoices" #~ msgstr "Criar Facturas" +#~ msgid "VAT" +#~ msgstr "IVA" + #~ msgid "Repairs" #~ msgstr "Reparações" diff --git a/addons/mrp_repair/i18n/pt_BR.po b/addons/mrp_repair/i18n/pt_BR.po index 1cd89664a02..6eb1ae72199 100644 --- a/addons/mrp_repair/i18n/pt_BR.po +++ b/addons/mrp_repair/i18n/pt_BR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "Atenção !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/ro.po b/addons/mrp_repair/i18n/ro.po index 8c91256235f..887b9708cde 100644 --- a/addons/mrp_repair/i18n/ro.po +++ b/addons/mrp_repair/i18n/ro.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -192,8 +192,8 @@ msgstr "Avertizare !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "TVA" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -843,6 +843,9 @@ msgstr "Niciun partener !" #~ " * Raport cotatie reparatii\n" #~ " * Note pentru tehnician si pentru clientul final\n" +#~ msgid "VAT" +#~ msgstr "TVA" + #~ msgid "Products Repairs Module - Manage All products Repairs" #~ msgstr "Modulul Reparatii Produse - Gestionează toate Reparatiile produselor" diff --git a/addons/mrp_repair/i18n/ru.po b/addons/mrp_repair/i18n/ru.po index 58751496f16..f41e1f1b5c9 100644 --- a/addons/mrp_repair/i18n/ru.po +++ b/addons/mrp_repair/i18n/ru.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/sl.po b/addons/mrp_repair/i18n/sl.po index 16a321ff926..c3e764f9843 100644 --- a/addons/mrp_repair/i18n/sl.po +++ b/addons/mrp_repair/i18n/sl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,8 +169,8 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "DDV" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -793,6 +793,9 @@ msgstr "" #~ msgid "Repair State" #~ msgstr "Stanje popravila" +#~ msgid "VAT" +#~ msgstr "DDV" + #~ msgid "Repairs" #~ msgstr "Popravila" diff --git a/addons/mrp_repair/i18n/sq.po b/addons/mrp_repair/i18n/sq.po index 61096ec3734..8343c292e16 100644 --- a/addons/mrp_repair/i18n/sq.po +++ b/addons/mrp_repair/i18n/sq.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -170,7 +170,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/sr.po b/addons/mrp_repair/i18n/sr.po index 7c89079387b..f66154ebcb9 100644 --- a/addons/mrp_repair/i18n/sr.po +++ b/addons/mrp_repair/i18n/sr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -186,8 +186,8 @@ msgstr "Upozorenje!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "PDV" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -823,6 +823,9 @@ msgstr "Nema partnera !" #~ msgid "Repair Order Ref" #~ msgstr "Ref.Nalog za Popravku" +#~ msgid "VAT" +#~ msgstr "PDV" + #~ msgid "Repair Fees line" #~ msgstr "Dodatne Linije Popravke" diff --git a/addons/mrp_repair/i18n/sr@latin.po b/addons/mrp_repair/i18n/sr@latin.po index 0e913194736..8a44ede0215 100644 --- a/addons/mrp_repair/i18n/sr@latin.po +++ b/addons/mrp_repair/i18n/sr@latin.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -186,8 +186,8 @@ msgstr "Upozorenje!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "PDV" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -817,6 +817,9 @@ msgstr "Nema partnera !" #~ msgid "Invalid model name in the action definition." #~ msgstr "Pogrešno ime modela u definiciji akcije." +#~ msgid "VAT" +#~ msgstr "PDV" + #~ msgid "Invalid XML for View Architecture!" #~ msgstr "Nevažeći XML za pregled arhitekture" diff --git a/addons/mrp_repair/i18n/sv.po b/addons/mrp_repair/i18n/sv.po index 81317b71571..d326c90147e 100644 --- a/addons/mrp_repair/i18n/sv.po +++ b/addons/mrp_repair/i18n/sv.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,8 +169,8 @@ msgstr "Varning !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "Moms" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -781,6 +781,9 @@ msgstr "" #~ msgstr "" #~ "Objektnamnet måste börja med x_ och får inte innehålla några specialtecken!" +#~ msgid "VAT" +#~ msgstr "Moms" + #~ msgid "Invalid XML for View Architecture!" #~ msgstr "Felaktig XML för Vyarkitektur!" diff --git a/addons/mrp_repair/i18n/tlh.po b/addons/mrp_repair/i18n/tlh.po index 562251fcba6..078acaa976e 100644 --- a/addons/mrp_repair/i18n/tlh.po +++ b/addons/mrp_repair/i18n/tlh.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/tr.po b/addons/mrp_repair/i18n/tr.po index 90945430991..8f1ed46113e 100644 --- a/addons/mrp_repair/i18n/tr.po +++ b/addons/mrp_repair/i18n/tr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -188,8 +188,8 @@ msgstr "Uyarı !" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "KDV" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -822,6 +822,9 @@ msgstr "Paydaş Yok !" #~ msgid "Repair State" #~ msgstr "Onarım Durum" +#~ msgid "VAT" +#~ msgstr "KDV" + #~ msgid "Repairs in quotation" #~ msgstr "Teklifdeki Onarımlar" diff --git a/addons/mrp_repair/i18n/uk.po b/addons/mrp_repair/i18n/uk.po index d6608c0a998..14bfa2e1d2a 100644 --- a/addons/mrp_repair/i18n/uk.po +++ b/addons/mrp_repair/i18n/uk.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/vi.po b/addons/mrp_repair/i18n/vi.po index 70f543586be..5b3f98caff0 100644 --- a/addons/mrp_repair/i18n/vi.po +++ b/addons/mrp_repair/i18n/vi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -170,7 +170,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/i18n/zh_CN.po b/addons/mrp_repair/i18n/zh_CN.po index 896df443961..a477872aa63 100644 --- a/addons/mrp_repair/i18n/zh_CN.po +++ b/addons/mrp_repair/i18n/zh_CN.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -177,8 +177,8 @@ msgstr "警告!" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" -msgstr "增值税" +msgid "Tax" +msgstr "" #. module: mrp_repair #: view:mrp.repair:0 @@ -785,6 +785,9 @@ msgstr "准备修理" msgid "No partner !" msgstr "没有输入业务伙伴" +#~ msgid "VAT" +#~ msgstr "增值税" + #~ msgid "Status" #~ msgstr "状态" diff --git a/addons/mrp_repair/i18n/zh_TW.po b/addons/mrp_repair/i18n/zh_TW.po index c6872a43a62..22b842543b1 100644 --- a/addons/mrp_repair/i18n/zh_TW.po +++ b/addons/mrp_repair/i18n/zh_TW.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: mrp_repair #: view:mrp.repair:0 @@ -169,7 +169,7 @@ msgstr "" #. module: mrp_repair #: report:repair.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: mrp_repair diff --git a/addons/mrp_repair/report/order.rml b/addons/mrp_repair/report/order.rml index 539ea69ba63..be5737d4b85 100644 --- a/addons/mrp_repair/report/order.rml +++ b/addons/mrp_repair/report/order.rml @@ -169,7 +169,7 @@ <para style="terp_tblheader_Details">Description</para> </td> <td> - <para style="terp_tblheader_Details_Centre">VAT</para> + <para style="terp_tblheader_Details_Centre">Tax</para> </td> <td> <para style="terp_tblheader_Details_Right">Quantity</para> @@ -278,7 +278,7 @@ <para style="terp_tblheader_Details">Description</para> </td> <td> - <para style="terp_tblheader_Details_Centre">VAT</para> + <para style="terp_tblheader_Details_Centre">Tax</para> </td> <td> <para style="terp_tblheader_Details_Right">Quantity</para> diff --git a/addons/pad/LICENSE b/addons/pad/LICENSE deleted file mode 100644 index ff64be448e9..00000000000 --- a/addons/pad/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -OpenERP, Open Source Management Solution -Copyright © 2010-2011 OpenERP SA (<http://openerp.com>). - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -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 Lice -along with this program. If not, see <http://www.gnu.org/licenses/>. diff --git a/addons/pad/__init__.py b/addons/pad/__init__.py index 663cac2cbf0..ee80bcaf8d0 100644 --- a/addons/pad/__init__.py +++ b/addons/pad/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import pad import res_company -import ir_attachment + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/pad/__openerp__.py b/addons/pad/__openerp__.py index 267f595c7e5..bc4159089d7 100644 --- a/addons/pad/__openerp__.py +++ b/addons/pad/__openerp__.py @@ -25,6 +25,7 @@ Lets the company customize which Pad installation should be used to link to new "static/src/xml/*.xml", ], 'images': ['static/src/img/pad_link_companies.jpeg'], + "css": ['static/src/css/etherpad.css',], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/pad/ir_attachment.py b/addons/pad/ir_attachment.py deleted file mode 100644 index ba8c88632dd..00000000000 --- a/addons/pad/ir_attachment.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -from osv import fields, osv -import random -import string - -class ir_attachment(osv.osv): - _inherit = 'ir.attachment' - - def pad_generate_url(self, cr, uid, model, id): - pad_url_template = self.pool.get('res.users').browse(cr,uid,[uid])[0].company_id.pad_url_template - s = string.ascii_uppercase + string.digits - salt = ''.join([s[random.randint(0, len(s) - 1)] for i in range(8)]) - template_vars = { - 'db' : cr.dbname, - 'model' : model, - 'id' : id, - 'salt' : salt, - 'name' : '', - } - return pad_url_template % template_vars - - def pad_get(self, cr, uid, model, id): - attachment = self.search(cr, uid, [('res_model', '=', model), ('res_id', '=', id), ('type', '=', 'url'), ('name', '=', 'Pad')]) - if attachment: - return self.read(cr, uid, attachment)[0]['url'] - else: - url = self.pad_generate_url(cr, uid, model, id) - self.create(cr, uid, { - 'res_model' : model, - 'res_id' : id, - 'type' : 'url', - 'name' : 'Pad', - 'url' : url, - }) - return url - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/pad/pad.py b/addons/pad/pad.py new file mode 100644 index 00000000000..40569fcd589 --- /dev/null +++ b/addons/pad/pad.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from osv import fields, osv +import random +import string +import urllib2 +from tools.translate import _ + +class pad_common(osv.osv_memory): + _name = 'pad.common' + _pad_fields = [] + def pad_generate_url(self, cr, uid, context=None): + pad_url_template = self.pool.get('res.users').browse(cr,uid, uid, context).company_id.pad_url_template + s = string.ascii_uppercase + string.digits + salt = ''.join([s[random.randint(0, len(s) - 1)] for i in range(8)]) + template_vars = { + 'db' : cr.dbname, + 'model' : self._name, + 'salt' : salt, + } + url = pad_url_template % template_vars + return url + + def copy(self, cr, uid, id, default=None, context=None): + if not default: + default = {} + update = [(field,self.pad_generate_url(cr, uid, context)) for field in self._pad_fields] + default.update(update) + return super(pad_common, self).copy(cr, uid, id, default, context) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/pad/res_company.py b/addons/pad/res_company.py index 0c1dc7c4929..bd955cd0481 100644 --- a/addons/pad/res_company.py +++ b/addons/pad/res_company.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- from osv import fields, osv +DEFAULT_PAD_TEMPLATE = 'http://beta.etherpad.org/p/%(db)s-%(model)s-%(salt)s' + class company_pad(osv.osv): _inherit = 'res.company' _columns = { - 'pad_url_template': fields.char('Pad URL Template', size=128, required=True, - help="Template used to generate pad URL."), + 'pad_url_template': fields.char('Pad URL Template', size=128, required=True, help="Template used to generate pad URL."), } _defaults = { - 'pad_url_template': 'http://ietherpad.com/%(db)s-%(model)s-%(id)d-%(salt)s-%(name)s' + 'pad_url_template': DEFAULT_PAD_TEMPLATE, } diff --git a/addons/pad/res_company.xml b/addons/pad/res_company.xml index 9b1c0656470..12f14b0f5bd 100644 --- a/addons/pad/res_company.xml +++ b/addons/pad/res_company.xml @@ -8,7 +8,7 @@ <field name="arch" type="xml"> <xpath expr="//group[@name='account_grp']" position="after"> <group string="Pads"> - <field name="pad_url_template"/> + <field name="pad_url_template" placeholder="e.g. http://beta.etherpad.org/p/%%(db)s-%%(model)s-%%(salt)s"/> </group> </xpath> </field> diff --git a/addons/pad/static/src/css/etherpad.css b/addons/pad/static/src/css/etherpad.css new file mode 100644 index 00000000000..a0240afcab7 --- /dev/null +++ b/addons/pad/static/src/css/etherpad.css @@ -0,0 +1,70 @@ +.oe_etherpad_head{ + width: 100%; + display: block; + padding-left:3px; + cursor:pointer; +} +.oe_etherpad_fullscreen div.oe_etherpad_head .oe_normal { + display: none; +} +.oe_etherpad_normal div.oe_etherpad_head .oe_fullscreen { + display: none; +} +.oe_etherpad_fullscreen { + position: fixed; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-color: white; + z-index:10001 !important; +} +.etherpad_default{ + width: 100%; + height: 450px; +} +.oe_etherpad_fullscreen .etherpad_default{ + height: 100% +} + +.etherpad_body{ + overflow:hidden; +} + +.etherpad_readonly{ + width: 100%; + height: 450px; + overflow:auto; +} +.etherpad_readonly ul,.etherpad_readonly ol{ + -webkit-margin-before: 1em; + -webkit-margin-after: 1em; + -webkit-margin-start: 0px; + -webkit-margin-end: 0px; + -webkit-padding-start: 40px !important; +} +.etherpad_readonly ul li{ + list-style-type: disc; +} +.etherpad_readonly ol li{ + list-style-type: decimal; +} +.etherpad_readonly .indent li{ + list-style-type: none !important; +} + +.etherpad_readonly{ + font-family: arial, sans-serif; + font-size: 13px; + line-height: 17px; +} + +.etherpad_readonly ul.indent { list-style-type: none !important; } +.etherpad_readonly ol li{ list-style-type: decimal !important; } +.etherpad_readonly ol ol li{ list-style-type: lower-latin !important; } +.etherpad_readonly ol ol ol li{ list-style-type: lower-roman !important; } +.etherpad_readonly ol ol ol ol li{ list-style-type: decimal !important; } +.etherpad_readonly ol ol ol ol ol li{ list-style-type: lower-latin !important; } +.etherpad_readonly ol ol ol ol ol ol li{ list-style-type: lower-roman !important; } +.etherpad_readonly ol ol ol ol ol ol ol li{ list-style-type: decimal !important; } +.etherpad_readonly ol ol ol ol ol ol ol ol li{ list-style-type: lower-latin !important; } diff --git a/addons/pad/static/src/js/pad.js b/addons/pad/static/src/js/pad.js index 2e5f8820ac4..6384eb72da8 100644 --- a/addons/pad/static/src/js/pad.js +++ b/addons/pad/static/src/js/pad.js @@ -1,18 +1,40 @@ openerp.pad = function(instance) { -instance.web.Sidebar = instance.web.Sidebar.extend({ - init: function(parent) { - this._super(parent); - this.add_items('other',[{ label: "Pad", callback: this.on_add_pad }]); - }, - on_add_pad: function() { +instance.web.form.FieldPad = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, { + template: 'FieldPad', + initialize_content: function() { var self = this; - var view = this.getParent(); - var model = new instance.web.Model('ir.attachment'); - model.call('pad_get', [view.model ,view.datarecord.id],{}).then(function(r) { - self.do_action({ type: "ir.actions.act_url", url: r }); + this.$textarea = undefined; + this.$element.find('div.oe_etherpad_head').click(function(ev) { + self.$element.toggleClass('oe_etherpad_fullscreen').toggleClass('oe_etherpad_normal'); }); - } + }, + set_value: function(value_) { + this._super(value_); + this.render_value(); + }, + render_value: function() { + var self = this; + var value = this.get('value'); + if(value !== false) { + var url = value.split('\n')[0]; + if (!this.get("effective_readonly")) { + var pad_username = this.session.username; + var code = '<iframe width="100%" height="100%" frameborder="0" src="'+url+'?showChat=false&userName='+pad_username+'"></iframe>'; + this.$element.find('div.oe_etherpad_default').html(code); + } else { + $.get(url+'/export/html').success(function(data) { + self.$element.html('<div class="etherpad_readonly">'+data+'</div>'); + }).error(function() { + self.$element.text('Unable to load pad'); + }); + } + } + }, +}); + +instance.web.form.widgets = instance.web.form.widgets.extend({ + 'pad': 'instance.web.form.FieldPad', }); }; diff --git a/addons/pad/static/src/xml/pad.xml b/addons/pad/static/src/xml/pad.xml index e922294d03d..e128fb183fb 100644 --- a/addons/pad/static/src/xml/pad.xml +++ b/addons/pad/static/src/xml/pad.xml @@ -2,4 +2,17 @@ <!-- vim:fdl=1: --> <templates id="template" xml:space="preserve"> + +<t t-name="FieldPad"> + <t t-if="!widget.get('effective_readonly')"> + <div class="oe_form_field_text oe_etherpad_normal"> + <div class="oe_etherpad_head"> + <span class="oe_normal">Fullscreen</span> + <span class="oe_fullscreen">Return to Record</span> + </div> + <div class="oe_etherpad_default" ></div> + </div> + </t> +</t> + </templates> diff --git a/addons/pad_project/__init__.py b/addons/pad_project/__init__.py index d6bf9f9551c..098de31a5a4 100644 --- a/addons/pad_project/__init__.py +++ b/addons/pad_project/__init__.py @@ -19,6 +19,6 @@ # ############################################################################## -import models +import project_task # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/pad_project/__openerp__.py b/addons/pad_project/__openerp__.py index 8ca761d2737..18c7d3191d3 100644 --- a/addons/pad_project/__openerp__.py +++ b/addons/pad_project/__openerp__.py @@ -20,7 +20,7 @@ ############################################################################## { - 'name': 'Specifications on PADs', + 'name': 'Pad on tasks', 'version': '1.0', "category": "Project Management", 'description': """ @@ -31,9 +31,9 @@ This module adds a PAD in all project kanban views 'website': 'http://www.openerp.com', 'depends': ['project', 'pad'], 'init_xml': [], - 'update_xml': ['models/project_task.xml'], + 'update_xml': ['project_task.xml'], 'demo_xml': [], 'installable': True, - 'auto_install': False, + 'auto_install': True, } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/pad_project/models/__init__.py b/addons/pad_project/models/__init__.py deleted file mode 100644 index 098de31a5a4..00000000000 --- a/addons/pad_project/models/__init__.py +++ /dev/null @@ -1,24 +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/>. -# -############################################################################## - -import project_task - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/pad_project/models/project_task.py b/addons/pad_project/models/project_task.py deleted file mode 100644 index c215aed1325..00000000000 --- a/addons/pad_project/models/project_task.py +++ /dev/null @@ -1,17 +0,0 @@ -# -*- coding: utf-8 -*- -from tools.translate import _ -from osv import fields, osv - -class task(osv.osv): - _inherit = "project.task" - - def pad_get(self, cr, uid, ids, context=None): - """Get pad action - """ - url = self.pool.get("ir.attachment").pad_get(cr, uid, self._name, ids[0]) - return { - 'type': 'ir.actions.act_url', - 'url': url - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/pad_project/models/project_task.xml b/addons/pad_project/models/project_task.xml deleted file mode 100644 index 3c734cf3b97..00000000000 --- a/addons/pad_project/models/project_task.xml +++ /dev/null @@ -1,15 +0,0 @@ -<openerp> - <data> - <record id="view_task_kanban_with_pad" model="ir.ui.view"> - <field name="name">project.task.kanban.pad</field> - <field name="model">project.task</field> - <field name="type">kanban</field> - <field name="inherit_id" ref="project.view_task_kanban"/> - <field name="arch" type="xml"> - <xpath expr="//span[@class='oe_kanban_project_times']" position="before"> - <a name="pad_get" string="Pad" type="object" class="oe_kanban_button" style="margin-left: 5px;"><b>PAD</b></a> - </xpath> - </field> - </record> - </data> -</openerp> diff --git a/addons/pad_project/project_task.py b/addons/pad_project/project_task.py new file mode 100644 index 00000000000..46c2c54cd46 --- /dev/null +++ b/addons/pad_project/project_task.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +from tools.translate import _ +from osv import fields, osv + +class task(osv.osv): + _name = "project.task" + _inherit = ["project.task",'pad.common'] + _pad_fields = ['description_pad'] + _columns = { + 'description_pad': fields.char('Description PAD', size=250) + } + _defaults = { + 'description_pad': lambda self, cr, uid, context: self.pad_generate_url(cr, uid, context), + } diff --git a/addons/pad_project/project_task.xml b/addons/pad_project/project_task.xml new file mode 100644 index 00000000000..d18f43a92f4 --- /dev/null +++ b/addons/pad_project/project_task.xml @@ -0,0 +1,15 @@ +<openerp> + <data> + <record id="view_task_form_with_pad" model="ir.ui.view"> + <field name="name">project.task.form.pad</field> + <field name="model">project.task</field> + <field name="type">form</field> + <field name="inherit_id" ref="project.view_task_form2"/> + <field name="arch" type="xml"> + <field name="description" position="replace"> + <field name="description_pad" attrs="{'readonly':[('state','=','done')]}" widget="pad"/> + </field> + </field> + </record> + </data> +</openerp> diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py index 4c0bb14cc10..96d344b197b 100644 --- a/addons/point_of_sale/__openerp__.py +++ b/addons/point_of_sale/__openerp__.py @@ -25,6 +25,7 @@ 'version': '1.0.1', 'category': 'Point Of Sale', "sequence": 6, + "summary": "Touchscreen to Manage your Shop", 'description': """ This module provides a quick and easy sale process. =================================================== diff --git a/addons/point_of_sale/i18n/point_of_sale.pot b/addons/point_of_sale/i18n/point_of_sale.pot index b0ff45bb671..0246ca65227 100644 --- a/addons/point_of_sale/i18n/point_of_sale.pot +++ b/addons/point_of_sale/i18n/point_of_sale.pot @@ -155,7 +155,7 @@ msgstr "" #. module: point_of_sale #: report:pos.lines:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: point_of_sale diff --git a/addons/point_of_sale/report/pos_lines.rml b/addons/point_of_sale/report/pos_lines.rml index 48eaca6c73b..be394ed4f16 100644 --- a/addons/point_of_sale/report/pos_lines.rml +++ b/addons/point_of_sale/report/pos_lines.rml @@ -150,7 +150,7 @@ <para style="terp_tblheader_Details">Description</para> </td> <td> - <para style="terp_tblheader_Details_Centre">VAT</para> + <para style="terp_tblheader_Details_Centre">Tax</para> </td> <td> <para style="terp_tblheader_Details_Right">Quantity</para> diff --git a/addons/product_manufacturer/i18n/nb.po b/addons/product_manufacturer/i18n/nb.po new file mode 100644 index 00000000000..8646155eb4a --- /dev/null +++ b/addons/product_manufacturer/i18n/nb.po @@ -0,0 +1,77 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:37+0000\n" +"PO-Revision-Date: 2012-07-22 21:31+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-07-23 05:17+0000\n" +"X-Generator: Launchpad (build 15654)\n" + +#. module: product_manufacturer +#: field:product.product,manufacturer_pref:0 +msgid "Manufacturer Product Code" +msgstr "" + +#. module: product_manufacturer +#: model:ir.model,name:product_manufacturer.model_product_product +#: field:product.manufacturer.attribute,product_id:0 +msgid "Product" +msgstr "Produkt" + +#. module: product_manufacturer +#: view:product.manufacturer.attribute:0 +msgid "Product Template Name" +msgstr "Produkt template navn" + +#. module: product_manufacturer +#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute +msgid "Product attributes" +msgstr "Produkt attributter" + +#. module: product_manufacturer +#: view:product.manufacturer.attribute:0 +#: view:product.product:0 +msgid "Product Attributes" +msgstr "" + +#. module: product_manufacturer +#: field:product.manufacturer.attribute,name:0 +msgid "Attribute" +msgstr "Attributt" + +#. module: product_manufacturer +#: field:product.manufacturer.attribute,value:0 +msgid "Value" +msgstr "Verdi" + +#. module: product_manufacturer +#: constraint:product.product:0 +msgid "Error: Invalid ean code" +msgstr "Feil: Ugyldig ean kode" + +#. module: product_manufacturer +#: view:product.product:0 +#: field:product.product,attribute_ids:0 +msgid "Attributes" +msgstr "Attributter" + +#. module: product_manufacturer +#: field:product.product,manufacturer_pname:0 +msgid "Manufacturer Product Name" +msgstr "Produsents produktnavn" + +#. module: product_manufacturer +#: view:product.product:0 +#: field:product.product,manufacturer:0 +msgid "Manufacturer" +msgstr "Produsent" diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py index dad67f5dc30..b6464610711 100644 --- a/addons/project/__openerp__.py +++ b/addons/project/__openerp__.py @@ -27,6 +27,7 @@ "website": "http://www.openerp.com", "category": "Project Management", "sequence": 8, + "summary": "Projects, Tasks", "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", "base_status", "product", "analytic", "board", "mail", "resource","web_kanban"], "description": """ diff --git a/addons/project/security/ir.model.access.csv b/addons/project/security/ir.model.access.csv index b6fd90c948f..3f79cfee3de 100644 --- a/addons/project/security/ir.model.access.csv +++ b/addons/project/security/ir.model.access.csv @@ -18,4 +18,3 @@ access_account_analytic_line_project,account.analytic.line project,analytic.mode access_project_task_history,project.task.history project,project.model_project_task_history,project.group_project_user,1,1,1,0
access_project_task_history_cumulative,project.task.history project,project.model_project_task_history_cumulative,project.group_project_manager,1,0,0,0
access_resource_calendar,project.resource_calendar manager,resource.model_resource_calendar,project.group_project_manager,1,0,0,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_gtd/__openerp__.py b/addons/project_gtd/__openerp__.py index d6fa568977a..c28bc6f732a 100644 --- a/addons/project_gtd/__openerp__.py +++ b/addons/project_gtd/__openerp__.py @@ -25,6 +25,7 @@ 'version': '1.0', 'category': 'Project Management', "sequence": 20, + "summary": "Personal Tasks", 'description': """ This module implements all concepts defined by the Getting Things Done methodology. =================================================================================== diff --git a/addons/project_issue/__openerp__.py b/addons/project_issue/__openerp__.py index 9037523d41e..fa7d002f571 100644 --- a/addons/project_issue/__openerp__.py +++ b/addons/project_issue/__openerp__.py @@ -25,6 +25,7 @@ 'version': '1.0', 'category': 'Project Management', "sequence": 22, + "summary": "Multi Level Support", 'description': """ This module provides Issues/Bugs Management in Project. ======================================================= diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 5f9b0a369ca..ca5bd9224f2 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -519,7 +519,7 @@ class project_issue(base_stage, osv.osv): # ------------------------------------------------------- # OpenChatter methods and notifications # ------------------------------------------------------- - + def message_get_subscribers(self, cr, uid, ids, context=None): """ Override to add responsible user. """ user_ids = super(project_issue, self).message_get_subscribers(cr, uid, ids, context=context) @@ -538,7 +538,7 @@ class project_issue(base_stage, osv.osv): return 'Project issue' def convert_to_task_send_note(self, cr, uid, ids, context=None): - message = _("Project issue has been <b>converted</b> in to task.") + message = _("Project issue has been <b>converted</b> into task.") return self.message_append_note(cr, uid, ids, body=message, context=context) def create_send_note(self, cr, uid, ids, context=None): diff --git a/addons/project_issue/security/ir.model.access.csv b/addons/project_issue/security/ir.model.access.csv index 789f93b5175..5799fb0b611 100644 --- a/addons/project_issue/security/ir.model.access.csv +++ b/addons/project_issue/security/ir.model.access.csv @@ -4,10 +4,8 @@ access_project_issue_report_all,project.issue.report,model_project_issue_report, 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_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_mail_message_issue_project_user,mail.message.user,mail.model_mail_message,project.group_project_user,1,1,1,0
access_crm_case_section,crm.case.section,crm.model_crm_case_section,project.group_project_user,1,0,0,0
access_project_issue_salesman,project.issue,model_project_issue,base.group_sale_salesman,1,0,0,0
access_project_issue_manager,project.issue,model_project_issue,base.group_sale_manager,1,0,0,0
diff --git a/addons/project_mailgate/__openerp__.py b/addons/project_mailgate/__openerp__.py index dad9fd09eae..34ace79c39e 100644 --- a/addons/project_mailgate/__openerp__.py +++ b/addons/project_mailgate/__openerp__.py @@ -44,7 +44,7 @@ There are two common alternatives to configure the mailbox integration: """, "init_xml": [], - "update_xml": ["security/ir.model.access.csv", + "update_xml": [ ], 'demo_xml': [ ], diff --git a/addons/project_mailgate/security/ir.model.access.csv b/addons/project_mailgate/security/ir.model.access.csv deleted file mode 100644 index 6c495169161..00000000000 --- a/addons/project_mailgate/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-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/purchase/__openerp__.py b/addons/purchase/__openerp__.py index d84408a690d..1f32f3a3145 100644 --- a/addons/purchase/__openerp__.py +++ b/addons/purchase/__openerp__.py @@ -25,6 +25,7 @@ 'version': '1.1', 'category': 'Purchase Management', "sequence": 19, + "summary": "Requests for Quotation, Invoicing Control", 'description': """ Purchase module is for generating a purchase order for purchase of goods from a supplier. ========================================================================================= diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index 968d8d1cecd..f28a0518669 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -24,6 +24,7 @@ 'version': '1.0', 'category': 'Sales Management', "sequence": 14, + "summary": "Quotations, Sales, Invoicing", 'description': """ The base module to manage quotations and sales orders. ====================================================== diff --git a/addons/sale/i18n/ar.po b/addons/sale/i18n/ar.po index d9c6ad71004..991e7876843 100644 --- a/addons/sale/i18n/ar.po +++ b/addons/sale/i18n/ar.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "تحذير !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "ضريبة القيمة المضافة" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2413,6 +2413,9 @@ msgstr "أصدر الفواتير بناءً على التسليم" #~ msgid "Configuration Progress" #~ msgstr "تقدم الإعدادات" +#~ msgid "VAT" +#~ msgstr "ضريبة القيمة المضافة" + #~ msgid " Month-1 " #~ msgstr " شهر- ١ " diff --git a/addons/sale/i18n/bg.po b/addons/sale/i18n/bg.po index f38ed8a9b86..1e2359a1936 100644 --- a/addons/sale/i18n/bg.po +++ b/addons/sale/i18n/bg.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "Предупреждение !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "ДДС" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2188,6 +2188,9 @@ msgstr "Фактура базирана на доставки" #~ msgid "Reference" #~ msgstr "Отпратка" +#~ msgid "VAT" +#~ msgstr "ДДС" + #~ msgid "" #~ "The sale order will automatically create the invoice proposition (draft " #~ "invoice). Ordered and delivered quantities may not be the same. You have to " diff --git a/addons/sale/i18n/bs.po b/addons/sale/i18n/bs.po index fc7cca1fec4..354439ed5f3 100644 --- a/addons/sale/i18n/bs.po +++ b/addons/sale/i18n/bs.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "PDV" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2286,6 +2286,9 @@ msgstr "Račun sa referencom na isporuku" #~ msgid "Our Salesman" #~ msgstr "Naš Prodavač" +#~ msgid "VAT" +#~ msgstr "PDV" + #~ msgid "Delivery Order Only" #~ msgstr "Samo isporuka" diff --git a/addons/sale/i18n/ca.po b/addons/sale/i18n/ca.po index 02c11057ac4..b47aeb04e49 100644 --- a/addons/sale/i18n/ca.po +++ b/addons/sale/i18n/ca.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Avís!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "IVA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2494,6 +2494,9 @@ msgstr "Factura des d'albarans" #~ "la comanda de venda. Aquesta transacció converteix la comanda de venda a " #~ "factures." +#~ msgid "VAT" +#~ msgstr "IVA" + #~ msgid "Make Invoice" #~ msgstr "Crea factura" diff --git a/addons/sale/i18n/cs.po b/addons/sale/i18n/cs.po index 03610618ecd..562aa26664f 100644 --- a/addons/sale/i18n/cs.po +++ b/addons/sale/i18n/cs.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" "X-Poedit-Language: Czech\n" #. module: sale @@ -87,8 +87,8 @@ msgstr "Varování !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "DPH" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2202,6 +2202,9 @@ msgstr "Faktura založená na dodávce" #~ msgid "Payment Before Delivery" #~ msgstr "Platba před doručením" +#~ msgid "VAT" +#~ msgstr "DPH" + #~ msgid "All at Once" #~ msgstr "Vše najednou" diff --git a/addons/sale/i18n/da.po b/addons/sale/i18n/da.po index 4dd21480065..ebea93e173b 100644 --- a/addons/sale/i18n/da.po +++ b/addons/sale/i18n/da.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Advarsel!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "Moms" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2165,6 +2165,9 @@ msgstr "År" msgid "Invoice Based on Deliveries" msgstr "" +#~ msgid "VAT" +#~ msgstr "Moms" + #~ msgid " Year " #~ msgstr " År " diff --git a/addons/sale/i18n/de.po b/addons/sale/i18n/de.po index 6e7eefbf3cf..aadc82565c6 100644 --- a/addons/sale/i18n/de.po +++ b/addons/sale/i18n/de.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Warnung !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "USt." +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2501,6 +2501,9 @@ msgstr "Rechnung durch Lieferung" #~ "Rechnung' angeklickt wird. Dieser Vorgang verschiebt den Verkaufsauftrag zu " #~ "den Rechnungen." +#~ msgid "VAT" +#~ msgstr "USt." + #~ msgid "Make Invoice" #~ msgstr "Erzeuge Rechnung" diff --git a/addons/sale/i18n/el.po b/addons/sale/i18n/el.po index 6dc0d1c8ca4..d919dc2071f 100644 --- a/addons/sale/i18n/el.po +++ b/addons/sale/i18n/el.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Προειδοποίηση!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "ΦΠΑ" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2227,6 +2227,9 @@ msgstr "Τιμολόγηση Βασισμένη Σε Παραδόσεις" #~ msgid "Reference" #~ msgstr "Παραπομπή" +#~ msgid "VAT" +#~ msgstr "ΦΠΑ" + #~ msgid "Procure Method" #~ msgstr "Μέθοδος Προμήθειας" diff --git a/addons/sale/i18n/es.po b/addons/sale/i18n/es.po index 5326d9f13ec..61f2fd976a5 100644 --- a/addons/sale/i18n/es.po +++ b/addons/sale/i18n/es.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -91,8 +91,8 @@ msgstr "¡Aviso!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "CIF/NIF" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -3000,3 +3000,6 @@ msgstr "Facturar desde albaranes" #~ msgid "Ordered Date" #~ msgstr "Fecha pedido" + +#~ msgid "VAT" +#~ msgstr "CIF/NIF" diff --git a/addons/sale/i18n/es_AR.po b/addons/sale/i18n/es_AR.po index 6e739de4b74..59284ec10cd 100644 --- a/addons/sale/i18n/es_AR.po +++ b/addons/sale/i18n/es_AR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "IVA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2170,6 +2170,9 @@ msgstr "Facturar desde órdenes de entrega" #~ msgid "Reference" #~ msgstr "Referencia" +#~ msgid "VAT" +#~ msgstr "IVA" + #~ msgid "" #~ "The sale order will automatically create the invoice proposition (draft " #~ "invoice). Ordered and delivered quantities may not be the same. You have to " diff --git a/addons/sale/i18n/es_CL.po b/addons/sale/i18n/es_CL.po index 9d20a2ec2c2..0ecd4353144 100644 --- a/addons/sale/i18n/es_CL.po +++ b/addons/sale/i18n/es_CL.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "¡Aviso!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "IVA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2274,6 +2274,9 @@ msgstr "Facturar desde guías de despacho" #~ msgid "Payment Before Delivery" #~ msgstr "Pago antes del envío" +#~ msgid "VAT" +#~ msgstr "IVA" + #~ msgid "All at Once" #~ msgstr "Todo a la vez" diff --git a/addons/sale/i18n/es_CR.po b/addons/sale/i18n/es_CR.po index fb4f3cd2a3c..3aa9d2262c8 100644 --- a/addons/sale/i18n/es_CR.po +++ b/addons/sale/i18n/es_CR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" "Language: \n" #. module: sale @@ -91,8 +91,8 @@ msgstr "¡Aviso!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "IVA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2382,6 +2382,9 @@ msgstr "Año" msgid "Invoice Based on Deliveries" msgstr "Facturar desde albaranes" +#~ msgid "VAT" +#~ msgstr "IVA" + #~ msgid "Do you really want to create the invoice(s) ?" #~ msgstr "¿Desea crear la(s) factura(s)?" diff --git a/addons/sale/i18n/es_EC.po b/addons/sale/i18n/es_EC.po index 361d651a110..94d4d958295 100644 --- a/addons/sale/i18n/es_EC.po +++ b/addons/sale/i18n/es_EC.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "¡Advertencia!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "VAT" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2567,6 +2567,9 @@ msgstr "Facturar desde albaranes" #~ msgid "Invalid model name in the action definition." #~ msgstr "Nombre de modelo no válido en la definición de la acción." +#~ msgid "VAT" +#~ msgstr "VAT" + #~ msgid "Configure Picking Policy for Sales Order" #~ msgstr "" #~ "Configurar la Politica de paquete de productos de los pedidos de ventas" diff --git a/addons/sale/i18n/et.po b/addons/sale/i18n/et.po index c373ae66644..799c62c28cf 100644 --- a/addons/sale/i18n/et.po +++ b/addons/sale/i18n/et.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: model:process.node,name:sale.process_node_deliveryorder0 @@ -155,8 +155,8 @@ msgstr "Hoiatus!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "KM" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2487,6 +2487,9 @@ msgstr "Arve kohaletoimetamiste põhjal" #~ msgid "My Sales Order" #~ msgstr "Minu müügikorraldus" +#~ msgid "VAT" +#~ msgstr "KM" + #~ msgid "Delivery, from the warehouse to the customer." #~ msgstr "Kohaletoimetamine laost kliendile." diff --git a/addons/sale/i18n/fi.po b/addons/sale/i18n/fi.po index a1149147d70..5ef37fea35e 100644 --- a/addons/sale/i18n/fi.po +++ b/addons/sale/i18n/fi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -91,8 +91,8 @@ msgstr "Varoitus !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "ALV" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2269,6 +2269,9 @@ msgstr "Lasku perustuen toimitettuihin" #~ msgid "Outgoing Products" #~ msgstr "Lähtevät tuotteet" +#~ msgid "VAT" +#~ msgstr "ALV" + #~ msgid "" #~ "The sale order will automatically create the invoice proposition (draft " #~ "invoice). Ordered and delivered quantities may not be the same. You have to " diff --git a/addons/sale/i18n/fr.po b/addons/sale/i18n/fr.po index a1207be3bab..97cd0137c97 100644 --- a/addons/sale/i18n/fr.po +++ b/addons/sale/i18n/fr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: code:addons/sale/sale.py:655 @@ -130,8 +130,8 @@ msgstr "Attention !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "TVA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2638,6 +2638,9 @@ msgstr "Facturation basé sur les livraisons" #~ msgid "Direct Delivery" #~ msgstr "Livraison directe" +#~ msgid "VAT" +#~ msgstr "TVA" + #~ msgid "Make Invoice" #~ msgstr "Créer facture" diff --git a/addons/sale/i18n/gl.po b/addons/sale/i18n/gl.po index 8d5aa806c80..81a6facac29 100644 --- a/addons/sale/i18n/gl.po +++ b/addons/sale/i18n/gl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -87,7 +87,7 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: sale diff --git a/addons/sale/i18n/hr.po b/addons/sale/i18n/hr.po index e802d9d3b75..f567ee5169a 100644 --- a/addons/sale/i18n/hr.po +++ b/addons/sale/i18n/hr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" "Language: hr\n" #. module: sale @@ -91,8 +91,8 @@ msgstr "Upozorenje !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "PDV" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2453,6 +2453,9 @@ msgstr "Faktura na temelju dostava" #~ "Račun se kreira kada se klikne na 'Kriraj Račun' nakon potvrde Prodajne " #~ "Narudžbe. Ova transakcija prenosi podatke Prodajne Narudžbe u Račune." +#~ msgid "VAT" +#~ msgstr "PDV" + #~ msgid "Sales order lines" #~ msgstr "Vezane Prodajne Narudžbe" diff --git a/addons/sale/i18n/hu.po b/addons/sale/i18n/hu.po index 97f0fe59171..d97ff49cc78 100644 --- a/addons/sale/i18n/hu.po +++ b/addons/sale/i18n/hu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Figyelem!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "ÁFA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2271,6 +2271,9 @@ msgstr "Számlák kiszállítások alapján" #~ msgid "Reference" #~ msgstr "Hivatkozás" +#~ msgid "VAT" +#~ msgstr "ÁFA" + #~ msgid "Net Price" #~ msgstr "Nettó ár" diff --git a/addons/sale/i18n/id.po b/addons/sale/i18n/id.po index 0fc14082a31..1439af92090 100644 --- a/addons/sale/i18n/id.po +++ b/addons/sale/i18n/id.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Peringatan !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "VAT" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2663,3 +2663,6 @@ msgstr "Invoice berdasarkan pengiriman" #~ msgid "Steps To Deliver a Sales Order" #~ msgstr "Langkah-langkah untuk pengiriman sales order." + +#~ msgid "VAT" +#~ msgstr "VAT" diff --git a/addons/sale/i18n/is.po b/addons/sale/i18n/is.po index 0f5351587ad..13a74f4c2a3 100644 --- a/addons/sale/i18n/is.po +++ b/addons/sale/i18n/is.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Aðvörun !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "VSK" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2171,3 +2171,6 @@ msgstr "" #: selection:sale.config.picking_policy,order_policy:0 msgid "Invoice Based on Deliveries" msgstr "" + +#~ msgid "VAT" +#~ msgstr "VSK" diff --git a/addons/sale/i18n/it.po b/addons/sale/i18n/it.po index ddfc2f09e6f..a4872985e17 100644 --- a/addons/sale/i18n/it.po +++ b/addons/sale/i18n/it.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "Attenzione!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "IVA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2500,6 +2500,9 @@ msgstr "Fattura basata su Consegne" #~ "l'ordine di vendita. Questa transazione crea una fattura sulla base " #~ "dell'ordine di vendita." +#~ msgid "VAT" +#~ msgstr "IVA" + #~ msgid "Make Invoice" #~ msgstr "Crea fattura" diff --git a/addons/sale/i18n/ja.po b/addons/sale/i18n/ja.po index 1e88262f8bb..50c3722876a 100644 --- a/addons/sale/i18n/ja.po +++ b/addons/sale/i18n/ja.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -87,8 +87,8 @@ msgstr "警告" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "消費税" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2283,3 +2283,6 @@ msgstr "年" #: selection:sale.config.picking_policy,order_policy:0 msgid "Invoice Based on Deliveries" msgstr "配達基準の請求書" + +#~ msgid "VAT" +#~ msgstr "消費税" diff --git a/addons/sale/i18n/ko.po b/addons/sale/i18n/ko.po index 594032bc105..8dadcc0c5de 100644 --- a/addons/sale/i18n/ko.po +++ b/addons/sale/i18n/ko.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -87,8 +87,8 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "VAT" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2205,6 +2205,9 @@ msgstr "베송에 기초한 인보이스" #~ msgid "Origin" #~ msgstr "기원" +#~ msgid "VAT" +#~ msgstr "VAT" + #~ msgid "" #~ "The sale order will automatically create the invoice proposition (draft " #~ "invoice). Ordered and delivered quantities may not be the same. You have to " diff --git a/addons/sale/i18n/lt.po b/addons/sale/i18n/lt.po index 47b6c90d2cc..305a1fd8664 100644 --- a/addons/sale/i18n/lt.po +++ b/addons/sale/i18n/lt.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Įspėjimas!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "PVM" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2185,6 +2185,9 @@ msgstr "Sąskaita faktūra iš pristatymo užsakymo" #~ msgid "Origin" #~ msgstr "Kilmė" +#~ msgid "VAT" +#~ msgstr "PVM" + #~ msgid "Automatic Declaration" #~ msgstr "Automatinis deklaravimas" diff --git a/addons/sale/i18n/lv.po b/addons/sale/i18n/lv.po index fba2e67f98f..eb1f56507dd 100644 --- a/addons/sale/i18n/lv.po +++ b/addons/sale/i18n/lv.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -87,8 +87,8 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "PVN" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2181,6 +2181,9 @@ msgstr "Rēķins pēc Piegādes" #~ msgid "Invoice after delivery" #~ msgstr "Rēķins pēc piegādes" +#~ msgid "VAT" +#~ msgstr "PVN" + #~ msgid "Outgoing Products" #~ msgstr "Izejošā Produkcija" diff --git a/addons/sale/i18n/mn.po b/addons/sale/i18n/mn.po index 547daf2db0b..2fc96bd7db5 100644 --- a/addons/sale/i18n/mn.po +++ b/addons/sale/i18n/mn.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -91,8 +91,8 @@ msgstr "Анхаар !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "НӨАТ" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2359,6 +2359,9 @@ msgstr "Жил" msgid "Invoice Based on Deliveries" msgstr "Хүргэлт дээр үндэслэн нэхэмжлэх" +#~ msgid "VAT" +#~ msgstr "НӨАТ" + #~ msgid "Sale Order" #~ msgstr "Борлуулах захиалга" diff --git a/addons/sale/i18n/nb.po b/addons/sale/i18n/nb.po index 6eb4c6e19e5..849ac26783a 100644 --- a/addons/sale/i18n/nb.po +++ b/addons/sale/i18n/nb.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -91,8 +91,8 @@ msgstr "Advarsel !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "MVA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2208,6 +2208,9 @@ msgstr "Faktura basert på leveringer" #~ msgid "Payment Before Delivery" #~ msgstr "Betaling før levering" +#~ msgid "VAT" +#~ msgstr "MVA" + #~ msgid "All at Once" #~ msgstr "Alt på en gang" diff --git a/addons/sale/i18n/nl.po b/addons/sale/i18n/nl.po index a074ba3dfb0..883d8f3b509 100644 --- a/addons/sale/i18n/nl.po +++ b/addons/sale/i18n/nl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -90,8 +90,8 @@ msgstr "Waarschuwing !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "BTW" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -372,7 +372,7 @@ msgid "" "Product Category or Fiscal Position is not defined !" msgstr "" "Er is geen categorie opbrengstrekening gedefinieerd in de standaard " -"eigenschappen van de productcategorie of fiscale situatie niet gedefinieerd !" +"eigenschappen van de productcategorie of fiscale positie niet gedefinieerd !" #. module: sale #: selection:sale.report,month:0 @@ -454,7 +454,7 @@ msgstr "Offertedatum" #. module: sale #: field:sale.order,fiscal_position:0 msgid "Fiscal Position" -msgstr "Fiscale situatie" +msgstr "Fiscale positie" #. module: sale #: view:sale.order:0 view:sale.order.line:0 field:sale.report,product_uom:0 @@ -1713,7 +1713,7 @@ msgstr "Handmatig in behandeling" #. module: sale #: field:sale.order.line,product_uom:0 msgid "Product UoM" -msgstr "Product meeteenheid" +msgstr "Product maateenheid" #. module: sale #: view:sale.order:0 @@ -2602,6 +2602,9 @@ msgstr "Factuur gebaseerd op leveringen" #~ "geven verkooporder. Deze handeling verplaatst de verkooporder naar de " #~ "facturen." +#~ msgid "VAT" +#~ msgstr "BTW" + #~ msgid "Make Invoice" #~ msgstr "Maak factuur" diff --git a/addons/sale/i18n/nl_BE.po b/addons/sale/i18n/nl_BE.po index 41853d574a2..49a664ab6cf 100644 --- a/addons/sale/i18n/nl_BE.po +++ b/addons/sale/i18n/nl_BE.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -91,8 +91,8 @@ msgstr "Waarschuwing" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "Btw" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2377,6 +2377,9 @@ msgstr "Factuur op basis van zendnota's" #~ msgid "Payment Before Delivery" #~ msgstr "Vooruitbetaling" +#~ msgid "VAT" +#~ msgstr "Btw" + #~ msgid "All at Once" #~ msgstr "Complete leveringen" diff --git a/addons/sale/i18n/oc.po b/addons/sale/i18n/oc.po index 2d1e93e607b..aea018e7d08 100644 --- a/addons/sale/i18n/oc.po +++ b/addons/sale/i18n/oc.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -87,7 +87,7 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: sale diff --git a/addons/sale/i18n/pl.po b/addons/sale/i18n/pl.po index e62531c01e4..9e75ec4af00 100644 --- a/addons/sale/i18n/pl.po +++ b/addons/sale/i18n/pl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -90,8 +90,8 @@ msgstr "Ostrzeżenie !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "NIP" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2709,6 +2709,9 @@ msgstr "Faktura wg dostaw" #~ msgid "Draft customer invoice, to be reviewed by accountant." #~ msgstr "Projekt faktury dla klienta do sprawdzenia księgowego" +#~ msgid "VAT" +#~ msgstr "NIP" + #, python-format #~ msgid "You cannot cancel a sale order line that has already been invoiced !" #~ msgstr "" diff --git a/addons/sale/i18n/pt.po b/addons/sale/i18n/pt.po index cf6bebbec98..6b0c5fd3ab4 100644 --- a/addons/sale/i18n/pt.po +++ b/addons/sale/i18n/pt.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -90,8 +90,8 @@ msgstr "Aviso !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "NIF" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -3086,3 +3086,6 @@ msgstr "Fatura Baseada em Entregas" #~ msgid "Do you really want to create the invoice(s) ?" #~ msgstr "Deseja realmente criar a fatura(s) ?" + +#~ msgid "VAT" +#~ msgstr "NIF" diff --git a/addons/sale/i18n/pt_BR.po b/addons/sale/i18n/pt_BR.po index 4b213456a7d..a8256204d5a 100644 --- a/addons/sale/i18n/pt_BR.po +++ b/addons/sale/i18n/pt_BR.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Aviso !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "Imposto" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2280,6 +2280,9 @@ msgstr "Notas Fiscais Baseadas nas Entregas" #~ msgid "Invoice after delivery" #~ msgstr "Faturar depois de entreguar" +#~ msgid "VAT" +#~ msgstr "Imposto" + #~ msgid "Origin" #~ msgstr "Origem" diff --git a/addons/sale/i18n/ro.po b/addons/sale/i18n/ro.po index 0f944a0d9e4..45738b758a6 100644 --- a/addons/sale/i18n/ro.po +++ b/addons/sale/i18n/ro.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -90,8 +90,8 @@ msgstr "Avertizare !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "TVA" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2393,6 +2393,9 @@ msgstr "Factura pe baza Livrarilor" #~ msgid "Origin" #~ msgstr "Origine" +#~ msgid "VAT" +#~ msgstr "TVA" + #~ msgid "Packing" #~ msgstr "Împachetare" diff --git a/addons/sale/i18n/ru.po b/addons/sale/i18n/ru.po index 2286a8b9971..a62be53408f 100644 --- a/addons/sale/i18n/ru.po +++ b/addons/sale/i18n/ru.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "Внимание!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "НДС" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2283,6 +2283,9 @@ msgstr "Счет основанный на доставках" #~ msgid "Sale Order" #~ msgstr "Заказ" +#~ msgid "VAT" +#~ msgstr "НДС" + #~ msgid "Sales order lines" #~ msgstr "Позиции заказа" diff --git a/addons/sale/i18n/sale.pot b/addons/sale/i18n/sale.pot index 64cd1ee5a8c..2c477d1d7a0 100644 --- a/addons/sale/i18n/sale.pot +++ b/addons/sale/i18n/sale.pot @@ -105,7 +105,7 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: sale diff --git a/addons/sale/i18n/sk.po b/addons/sale/i18n/sk.po index f385f207254..55c638674d0 100644 --- a/addons/sale/i18n/sk.po +++ b/addons/sale/i18n/sk.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -87,8 +87,8 @@ msgstr "Varovanie!" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "DPH" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2252,6 +2252,9 @@ msgstr "" #~ msgid "Packing Policy" #~ msgstr "Podmienky balenia" +#~ msgid "VAT" +#~ msgstr "DPH" + #~ msgid "Invalid XML for View Architecture!" #~ msgstr "Neplatné XML pre zobrazenie architektúry!" diff --git a/addons/sale/i18n/sl.po b/addons/sale/i18n/sl.po index 3df83d235aa..cee0e17d3ae 100644 --- a/addons/sale/i18n/sl.po +++ b/addons/sale/i18n/sl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "Opozorilo !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "DDV" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2294,6 +2294,9 @@ msgstr "Račun na Podlagi Dobav" #~ msgid "Direct Delivery" #~ msgstr "Direktna dostava" +#~ msgid "VAT" +#~ msgstr "DDV" + #~ msgid "Sales order lines" #~ msgstr "Postavke prodajnega naloga" diff --git a/addons/sale/i18n/sq.po b/addons/sale/i18n/sq.po index 5e94d3956b8..5bed8cb0ccb 100644 --- a/addons/sale/i18n/sq.po +++ b/addons/sale/i18n/sq.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 05:59+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:06+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -87,7 +87,7 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: sale diff --git a/addons/sale/i18n/sr.po b/addons/sale/i18n/sr.po index 572ce524c7d..4cad9147ec7 100644 --- a/addons/sale/i18n/sr.po +++ b/addons/sale/i18n/sr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Upozorenje !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "PDV" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2459,6 +2459,9 @@ msgstr "Račun na osnovu isporuke" #~ "Račun je kreiran kada je kliknuto 'Kreiranje račun' nakon potvrde naloga za " #~ "prodaju. Ovaj proces prenosi nalog za prodaju do računa." +#~ msgid "VAT" +#~ msgstr "PDV" + #~ msgid "Delivery Order Only" #~ msgstr "Isključivo nalog za isporuku" diff --git a/addons/sale/i18n/sr@latin.po b/addons/sale/i18n/sr@latin.po index 9350bb4ed1e..befa922b816 100644 --- a/addons/sale/i18n/sr@latin.po +++ b/addons/sale/i18n/sr@latin.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:01+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Upozorenje !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "PDV" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2185,6 +2185,9 @@ msgstr "Račun na osnovu isporuke" #~ msgid "Payment Before Delivery" #~ msgstr "Plaćanje pre isporuke" +#~ msgid "VAT" +#~ msgstr "PDV" + #~ msgid "All at Once" #~ msgstr "Sve odjednom" diff --git a/addons/sale/i18n/sv.po b/addons/sale/i18n/sv.po index 0fa027ff3b1..f96295a5be7 100644 --- a/addons/sale/i18n/sv.po +++ b/addons/sale/i18n/sv.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -90,8 +90,8 @@ msgstr "Varning !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "Moms" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2480,6 +2480,9 @@ msgstr "Fakturera utgående ifrån leveranser" #~ msgid "Sales Process" #~ msgstr "Kundorderprocess" +#~ msgid "VAT" +#~ msgstr "Moms" + #~ msgid "Delivery Order Only" #~ msgstr "Endast leveransorder" diff --git a/addons/sale/i18n/th.po b/addons/sale/i18n/th.po index fadb12bd016..2c0e2691dfd 100644 --- a/addons/sale/i18n/th.po +++ b/addons/sale/i18n/th.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -87,8 +87,8 @@ msgstr "คำเตือน !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "ภาษีมูลค่าเพิ่ม (VAT)" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2166,6 +2166,9 @@ msgstr "" #~ msgid "All at Once" #~ msgstr "ทั้วหมดในครั้งเดียว" +#~ msgid "VAT" +#~ msgstr "ภาษีมูลค่าเพิ่ม (VAT)" + #~ msgid "Configure" #~ msgstr "ตั้งค่า" diff --git a/addons/sale/i18n/tlh.po b/addons/sale/i18n/tlh.po index 143197536b4..cdff94156e4 100644 --- a/addons/sale/i18n/tlh.po +++ b/addons/sale/i18n/tlh.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -86,7 +86,7 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" +msgid "Tax" msgstr "" #. module: sale diff --git a/addons/sale/i18n/tr.po b/addons/sale/i18n/tr.po index 0f4e6b89cc8..d68060cf433 100644 --- a/addons/sale/i18n/tr.po +++ b/addons/sale/i18n/tr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -88,8 +88,8 @@ msgstr "Uyarı" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "KDV" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2252,6 +2252,9 @@ msgstr "Teslimatlara Bağlı Fatura" #~ msgid "Reference" #~ msgstr "Referans" +#~ msgid "VAT" +#~ msgstr "KDV" + #~ msgid "Net Price" #~ msgstr "Net Fiyat" diff --git a/addons/sale/i18n/uk.po b/addons/sale/i18n/uk.po index a5950553653..bfe3683cb29 100644 --- a/addons/sale/i18n/uk.po +++ b/addons/sale/i18n/uk.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -86,8 +86,8 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "ПДВ" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2284,6 +2284,9 @@ msgstr "" #~ msgid "Direct Delivery" #~ msgstr "Пряма Доставка" +#~ msgid "VAT" +#~ msgstr "ПДВ" + #~ msgid "Make Invoice" #~ msgstr "Виписати інвойс" diff --git a/addons/sale/i18n/vi.po b/addons/sale/i18n/vi.po index fb3bc012541..a94a024cc73 100644 --- a/addons/sale/i18n/vi.po +++ b/addons/sale/i18n/vi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -89,8 +89,8 @@ msgstr "Cảnh báo !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "Thuế GTGT" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2611,6 +2611,9 @@ msgstr "Hóa đơn dựa theo việc giao hàng" #~ msgid "Allows you to compute delivery costs on your quotations." #~ msgstr "Cho phép bạn tính toán chi phí giao hàng trên Đơn chào hàng của bạn" +#~ msgid "VAT" +#~ msgstr "Thuế GTGT" + #~ msgid "Configure" #~ msgstr "Cấu hình" diff --git a/addons/sale/i18n/zh_CN.po b/addons/sale/i18n/zh_CN.po index fbd99435bf9..dc70e1ecc7d 100644 --- a/addons/sale/i18n/zh_CN.po +++ b/addons/sale/i18n/zh_CN.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -86,8 +86,8 @@ msgstr "警告 !" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "增值税" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2195,6 +2195,9 @@ msgstr "年" msgid "Invoice Based on Deliveries" msgstr "发票基于送货单" +#~ msgid "VAT" +#~ msgstr "增值税" + #~ msgid "Draft Invoice" #~ msgstr "发票草稿" diff --git a/addons/sale/i18n/zh_TW.po b/addons/sale/i18n/zh_TW.po index 75d604ae074..99d07991b52 100644 --- a/addons/sale/i18n/zh_TW.po +++ b/addons/sale/i18n/zh_TW.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-14 06:00+0000\n" -"X-Generator: Launchpad (build 15614)\n" +"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n" +"X-Generator: Launchpad (build 15654)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -86,8 +86,8 @@ msgstr "" #. module: sale #: report:sale.order:0 -msgid "VAT" -msgstr "增值稅(VAT)" +msgid "Tax" +msgstr "" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -2379,6 +2379,9 @@ msgstr "根據交貨的發票" #~ msgid "Direct Delivery" #~ msgstr "直接交貨" +#~ msgid "VAT" +#~ msgstr "增值稅(VAT)" + #~ msgid "Make Invoice" #~ msgstr "開立發票" diff --git a/addons/sale/report/sale_order.rml b/addons/sale/report/sale_order.rml index 3a3bfc55042..8fecc17e62c 100644 --- a/addons/sale/report/sale_order.rml +++ b/addons/sale/report/sale_order.rml @@ -134,7 +134,7 @@ <para style="terp_tblheader_Details">Description</para> </td> <td> - <para style="terp_tblheader_Details_Centre">VAT</para> + <para style="terp_tblheader_Details_Centre">Tax</para> </td> <td> <para style="terp_tblheader_Details_Right">Quantity</para> @@ -236,7 +236,7 @@ <para style="terp_tblheader_Details">Description</para> </td> <td> - <para style="terp_tblheader_Details_Centre">VAT</para> + <para style="terp_tblheader_Details_Centre">Tax</para> </td> <td> <para style="terp_tblheader_Details_Right">Quantity</para> diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 03a2f35cbbe..3f7f7efa43b 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -3,10 +3,13 @@ <data> <!-- Top menu item --> + <!-- + This menu item's purpose is to overwrite another one defined in + the base module in order to set new groups. + --> <menuitem name="Sales" id="base.menu_base_partner" - groups="base.group_sale_salesman,base.group_sale_manager" - sequence="30"/> + groups="base.group_sale_salesman,base.group_sale_manager"/> <menuitem id="base.menu_product" name="Products" parent="base.menu_base_partner" sequence="9"/> diff --git a/addons/sale_analytic_plans/i18n/nb.po b/addons/sale_analytic_plans/i18n/nb.po new file mode 100644 index 00000000000..c234f234d72 --- /dev/null +++ b/addons/sale_analytic_plans/i18n/nb.po @@ -0,0 +1,28 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:37+0000\n" +"PO-Revision-Date: 2012-07-22 21:14+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-07-23 05:17+0000\n" +"X-Generator: Launchpad (build 15654)\n" + +#. module: sale_analytic_plans +#: field:sale.order.line,analytics_id:0 +msgid "Analytic Distribution" +msgstr "Analytisk Distribusjon" + +#. module: sale_analytic_plans +#: model:ir.model,name:sale_analytic_plans.model_sale_order_line +msgid "Sales Order Line" +msgstr "Salgsordrelinje" diff --git a/addons/sale_margin/i18n/nb.po b/addons/sale_margin/i18n/nb.po new file mode 100644 index 00000000000..7db7e12a698 --- /dev/null +++ b/addons/sale_margin/i18n/nb.po @@ -0,0 +1,52 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:37+0000\n" +"PO-Revision-Date: 2012-07-22 21:27+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-07-23 05:17+0000\n" +"X-Generator: Launchpad (build 15654)\n" + +#. module: sale_margin +#: sql_constraint:sale.order:0 +msgid "Order Reference must be unique per Company!" +msgstr "Ordrereferanse må være unik pr. firma!" + +#. module: sale_margin +#: field:sale.order.line,purchase_price:0 +msgid "Cost Price" +msgstr "Kostpris" + +#. module: sale_margin +#: model:ir.model,name:sale_margin.model_sale_order +msgid "Sales Order" +msgstr "Salgsordre" + +#. module: sale_margin +#: help:sale.order,margin:0 +msgid "" +"It gives profitability by calculating the difference between the Unit Price " +"and Cost Price." +msgstr "" +"Fortjeneste oppnås ved å beregne differansen mellom enhetspris og kostpris" + +#. module: sale_margin +#: field:sale.order,margin:0 +#: field:sale.order.line,margin:0 +msgid "Margin" +msgstr "Margin" + +#. module: sale_margin +#: model:ir.model,name:sale_margin.model_sale_order_line +msgid "Sales Order Line" +msgstr "Salgsordrelinje" diff --git a/addons/sale_mrp/i18n/nb.po b/addons/sale_mrp/i18n/nb.po new file mode 100644 index 00000000000..4b6589b24cc --- /dev/null +++ b/addons/sale_mrp/i18n/nb.po @@ -0,0 +1,53 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:37+0000\n" +"PO-Revision-Date: 2012-07-22 20:19+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-07-23 05:17+0000\n" +"X-Generator: Launchpad (build 15654)\n" + +#. module: sale_mrp +#: help:mrp.production,sale_ref:0 +msgid "Indicate the Customer Reference from sales order." +msgstr "Viser kundereferansen fra salgsordren" + +#. module: sale_mrp +#: field:mrp.production,sale_ref:0 +msgid "Sales Reference" +msgstr "Salgsreferanse" + +#. module: sale_mrp +#: model:ir.model,name:sale_mrp.model_mrp_production +msgid "Manufacturing Order" +msgstr "Produksjonsordre" + +#. module: sale_mrp +#: field:mrp.production,sale_name:0 +msgid "Sales Name" +msgstr "Salgsnavn" + +#. module: sale_mrp +#: sql_constraint:mrp.production:0 +msgid "Reference must be unique per Company!" +msgstr "Referanse må være unik pr firma!" + +#. module: sale_mrp +#: constraint:mrp.production:0 +msgid "Order quantity cannot be negative or zero!" +msgstr "Ordrekvantum kan ikke være negativt eller null!" + +#. module: sale_mrp +#: help:mrp.production,sale_name:0 +msgid "Indicate the name of sales order." +msgstr "Angi navnet på salgsordren." diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py index 50bcb23df7b..2eaaa1ffb69 100644 --- a/addons/stock/__openerp__.py +++ b/addons/stock/__openerp__.py @@ -23,6 +23,7 @@ "name" : "Warehouse Management", "version" : "1.1", "author" : "OpenERP SA", + "summary": "Incoming Shipments, Deliveries, Inventory, Stock", "description" : """ OpenERP Inventory Management module can manage multi-warehouses, multi and structured stock locations. ====================================================================================================== diff --git a/addons/stock_location/i18n/nb.po b/addons/stock_location/i18n/nb.po new file mode 100644 index 00000000000..9ee7cc50fd3 --- /dev/null +++ b/addons/stock_location/i18n/nb.po @@ -0,0 +1,400 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:37+0000\n" +"PO-Revision-Date: 2012-07-22 21:37+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-07-23 05:17+0000\n" +"X-Generator: Launchpad (build 15654)\n" + +#. module: stock_location +#: selection:product.pulled.flow,picking_type:0 +#: selection:stock.location.path,picking_type:0 +msgid "Sending Goods" +msgstr "Vareforsendelse" + +#. module: stock_location +#: view:product.product:0 +msgid "Pulled Paths" +msgstr "" + +#. module: stock_location +#: selection:product.pulled.flow,type_proc:0 +msgid "Move" +msgstr "Bevegelse" + +#. module: stock_location +#: model:ir.model,name:stock_location.model_stock_location_path +msgid "Pushed Flows" +msgstr "" + +#. module: stock_location +#: selection:stock.location.path,auto:0 +msgid "Automatic No Step Added" +msgstr "" + +#. module: stock_location +#: view:product.product:0 +msgid "Parameters" +msgstr "Parametere" + +#. module: stock_location +#: field:product.pulled.flow,location_src_id:0 +#: field:stock.location.path,location_from_id:0 +msgid "Source Location" +msgstr "Kildelokasjon" + +#. module: stock_location +#: help:product.pulled.flow,cancel_cascade:0 +msgid "Allow you to cancel moves related to the product pull flow" +msgstr "" + +#. module: stock_location +#: model:ir.model,name:stock_location.model_product_pulled_flow +#: field:product.product,flow_pull_ids:0 +msgid "Pulled Flows" +msgstr "" + +#. module: stock_location +#: constraint:stock.move:0 +msgid "You must assign a production lot for this product" +msgstr "Du må velge en produksjonslot/batch for dette produktet" + +#. module: stock_location +#: help:product.pulled.flow,location_src_id:0 +msgid "Location used by Destination Location to supply" +msgstr "" + +#. module: stock_location +#: selection:product.pulled.flow,picking_type:0 +#: selection:stock.location.path,picking_type:0 +msgid "Internal" +msgstr "Intern" + +#. module: stock_location +#: code:addons/stock_location/procurement_pull.py:98 +#, python-format +msgid "" +"Pulled procurement coming from original location %s, pull rule %s, via " +"original Procurement %s (#%d)" +msgstr "" + +#. module: stock_location +#: model:ir.model,name:stock_location.model_stock_location +msgid "Location" +msgstr "Lokasjon" + +#. module: stock_location +#: field:product.pulled.flow,invoice_state:0 +#: field:stock.location.path,invoice_state:0 +msgid "Invoice Status" +msgstr "Fakturastatus" + +#. module: stock_location +#: help:stock.location.path,auto:0 +msgid "" +"This is used to define paths the product has to follow within the location " +"tree.\n" +"The 'Automatic Move' value will create a stock move after the current one " +"that will be validated automatically. With 'Manual Operation', the stock " +"move has to be validated by a worker. With 'Automatic No Step Added', the " +"location is replaced in the original move." +msgstr "" + +#. module: stock_location +#: view:product.product:0 +msgid "Conditions" +msgstr "Vilkår" + +#. module: stock_location +#: model:stock.location,name:stock_location.location_pack_zone +msgid "Pack Zone" +msgstr "" + +#. module: stock_location +#: model:stock.location,name:stock_location.location_gate_b +msgid "Gate B" +msgstr "Gate B" + +#. module: stock_location +#: model:stock.location,name:stock_location.location_gate_a +msgid "Gate A" +msgstr "Gate A" + +#. module: stock_location +#: selection:product.pulled.flow,type_proc:0 +msgid "Buy" +msgstr "Kjøp" + +#. module: stock_location +#: view:product.product:0 +msgid "Pushed flows" +msgstr "" + +#. module: stock_location +#: model:stock.location,name:stock_location.location_dispatch_zone +msgid "Dispatch Zone" +msgstr "" + +#. module: stock_location +#: model:ir.model,name:stock_location.model_stock_move +msgid "Stock Move" +msgstr "Lagerbevegelse" + +#. module: stock_location +#: view:product.product:0 +msgid "Pulled flows" +msgstr "" + +#. module: stock_location +#: field:product.pulled.flow,company_id:0 +#: field:stock.location.path,company_id:0 +msgid "Company" +msgstr "Firma" + +#. module: stock_location +#: view:product.product:0 +msgid "Logistics Flows" +msgstr "" + +#. module: stock_location +#: help:stock.move,cancel_cascade:0 +msgid "If checked, when this move is cancelled, cancel the linked move too" +msgstr "" + +#. module: stock_location +#: selection:product.pulled.flow,type_proc:0 +msgid "Produce" +msgstr "Produser" + +#. module: stock_location +#: selection:product.pulled.flow,procure_method:0 +msgid "Make to Order" +msgstr "Produser på bestilling(Make to order)" + +#. module: stock_location +#: selection:product.pulled.flow,procure_method:0 +msgid "Make to Stock" +msgstr "Produser til lager" + +#. module: stock_location +#: field:product.pulled.flow,partner_address_id:0 +msgid "Partner Address" +msgstr "Partneradresse" + +#. module: stock_location +#: selection:product.pulled.flow,invoice_state:0 +#: selection:stock.location.path,invoice_state:0 +msgid "To Be Invoiced" +msgstr "Klare til fakturering" + +#. module: stock_location +#: help:stock.location.path,delay:0 +msgid "Number of days to do this transition" +msgstr "" + +#. module: stock_location +#: help:product.pulled.flow,name:0 +msgid "This field will fill the packing Origin and the name of its moves" +msgstr "" + +#. module: stock_location +#: field:product.pulled.flow,type_proc:0 +msgid "Type of Procurement" +msgstr "" + +#. module: stock_location +#: help:product.pulled.flow,company_id:0 +msgid "Is used to know to which company belong packings and moves" +msgstr "" + +#. module: stock_location +#: field:product.pulled.flow,name:0 +msgid "Name" +msgstr "Navn" + +#. module: stock_location +#: help:product.product,path_ids:0 +msgid "" +"These rules set the right path of the product in the whole location tree." +msgstr "" + +#. module: stock_location +#: constraint:stock.move:0 +msgid "You can not move products from or to a location of the type view." +msgstr "Kan ikke flytte produkter til eller fra en lokasjon av typen view." + +#. module: stock_location +#: selection:stock.location.path,auto:0 +msgid "Manual Operation" +msgstr "Manuell operasjon" + +#. module: stock_location +#: model:ir.model,name:stock_location.model_product_product +#: field:product.pulled.flow,product_id:0 +msgid "Product" +msgstr "Produkt" + +#. module: stock_location +#: field:product.pulled.flow,procure_method:0 +msgid "Procure Method" +msgstr "Innkjøpsmetode" + +#. module: stock_location +#: field:product.pulled.flow,picking_type:0 +#: field:stock.location.path,picking_type:0 +msgid "Shipping Type" +msgstr "Forsendelestype" + +#. module: stock_location +#: help:product.pulled.flow,procure_method:0 +msgid "" +"'Make to Stock': When needed, take from the stock or wait until re-" +"supplying. 'Make to Order': When needed, purchase or produce for the " +"procurement request." +msgstr "" +"'Make to Stock': When needed, take from the stock or wait until re-" +"supplying. 'Make to Order': When needed, purchase or produce for the " +"procurement request." + +#. module: stock_location +#: help:product.pulled.flow,location_id:0 +msgid "Is the destination location that needs supplying" +msgstr "" + +#. module: stock_location +#: field:stock.location.path,product_id:0 +msgid "Products" +msgstr "Produkter" + +#. module: stock_location +#: code:addons/stock_location/procurement_pull.py:118 +#, python-format +msgid "Pulled from another location via procurement %d" +msgstr "" + +#. module: stock_location +#: model:stock.location,name:stock_location.stock_location_qualitytest0 +msgid "Quality Control" +msgstr "Kvalitetskontroll" + +#. module: stock_location +#: selection:product.pulled.flow,invoice_state:0 +#: selection:stock.location.path,invoice_state:0 +msgid "Not Applicable" +msgstr "Ikke tilgjengelig" + +#. module: stock_location +#: field:stock.location.path,delay:0 +msgid "Delay (days)" +msgstr "Forsinkelse (dager)" + +#. module: stock_location +#: code:addons/stock_location/procurement_pull.py:67 +#, python-format +msgid "" +"Picking for pulled procurement coming from original location %s, pull rule " +"%s, via original Procurement %s (#%d)" +msgstr "" + +#. module: stock_location +#: field:product.product,path_ids:0 +msgid "Pushed Flow" +msgstr "" + +#. module: stock_location +#: code:addons/stock_location/procurement_pull.py:89 +#, python-format +msgid "" +"Move for pulled procurement coming from original location %s, pull rule %s, " +"via original Procurement %s (#%d)" +msgstr "" + +#. module: stock_location +#: constraint:stock.move:0 +msgid "You try to assign a lot which is not from the same product" +msgstr "Du forsøker å angi en lot som ikke tilhører samme produkt" + +#. module: stock_location +#: model:ir.model,name:stock_location.model_procurement_order +msgid "Procurement" +msgstr "Innkjøp" + +#. module: stock_location +#: field:product.pulled.flow,location_id:0 +#: field:stock.location.path,location_dest_id:0 +msgid "Destination Location" +msgstr "Destinasjons Lokasjon" + +#. module: stock_location +#: field:stock.location.path,auto:0 +#: selection:stock.location.path,auto:0 +msgid "Automatic Move" +msgstr "Automatisk bevegelse" + +#. module: stock_location +#: selection:product.pulled.flow,picking_type:0 +#: selection:stock.location.path,picking_type:0 +msgid "Getting Goods" +msgstr "" + +#. module: stock_location +#: view:product.product:0 +msgid "Action Type" +msgstr "Handlingstype" + +#. module: stock_location +#: constraint:product.product:0 +msgid "Error: Invalid ean code" +msgstr "Feil: Ugyldig ean kode" + +#. module: stock_location +#: help:product.pulled.flow,picking_type:0 +#: help:stock.location.path,picking_type:0 +msgid "" +"Depending on the company, choose whatever you want to receive or send " +"products" +msgstr "" + +#. module: stock_location +#: model:stock.location,name:stock_location.location_order +msgid "Order Processing" +msgstr "" + +#. module: stock_location +#: field:stock.location.path,name:0 +msgid "Operation" +msgstr "Handling" + +#. module: stock_location +#: view:stock.location.path:0 +msgid "Location Paths" +msgstr "" + +#. module: stock_location +#: field:product.pulled.flow,journal_id:0 +#: field:stock.location.path,journal_id:0 +msgid "Journal" +msgstr "Journal" + +#. module: stock_location +#: field:product.pulled.flow,cancel_cascade:0 +#: field:stock.move,cancel_cascade:0 +msgid "Cancel Cascade" +msgstr "" + +#. module: stock_location +#: selection:product.pulled.flow,invoice_state:0 +#: selection:stock.location.path,invoice_state:0 +msgid "Invoiced" +msgstr "Fakturert" |
