[MERGE]:merged with parent
bzr revid: apa@tinyerp.com-20120723051848-3rmbt5zodc7g2euq
|
@ -1788,7 +1788,7 @@ class account_tax_code(osv.osv):
|
||||||
'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
|
'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
|
||||||
'company_id': fields.many2one('res.company', 'Company', required=True),
|
'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.'),
|
'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'"),
|
'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'),
|
'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."),
|
'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 VAT 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."),
|
'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
|
||||||
'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
|
'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
|
||||||
|
|
||||||
# Same fields for refund invoices
|
# 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_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 VAT 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_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."),
|
'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"),
|
'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):
|
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.
|
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:
|
RETURN:
|
||||||
[ tax ]
|
[ tax ]
|
||||||
|
@ -2673,7 +2673,7 @@ class account_tax_code_template(osv.osv):
|
||||||
'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
|
'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
|
||||||
'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Child Codes'),
|
'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Child Codes'),
|
||||||
'sign': fields.float('Sign For Parent', required=True),
|
'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 = {
|
_defaults = {
|
||||||
|
@ -2788,17 +2788,17 @@ class account_tax_template(osv.osv):
|
||||||
'python_applicable':fields.text('Python Code'),
|
'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."),
|
'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 VAT 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."),
|
'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
|
||||||
'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
|
'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
|
||||||
|
|
||||||
# Same fields for refund invoices
|
# 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_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 VAT 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_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."),
|
'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."),
|
'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."),
|
||||||
|
|
|
@ -327,7 +327,7 @@ class account_move_line(osv.osv):
|
||||||
|
|
||||||
if account and ((not fields) or ('debit' in fields) or ('credit' in fields)):
|
if account and ((not fields) or ('debit' in fields) or ('credit' in fields)):
|
||||||
data['account_id'] = account.id
|
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:
|
if account.tax_ids:
|
||||||
taxes = fiscal_pos_obj.map_tax(cr, uid, part and part.property_account_position or False, 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)
|
tax = tax_obj.browse(cr, uid, taxes)
|
||||||
|
@ -1347,7 +1347,7 @@ class account_move_line(osv.osv):
|
||||||
}
|
}
|
||||||
if data['tax_code_id']:
|
if data['tax_code_id']:
|
||||||
self.create(cr, uid, data, context)
|
self.create(cr, uid, data, context)
|
||||||
#create the VAT movement
|
#create the Tax movement
|
||||||
data = {
|
data = {
|
||||||
'move_id': vals['move_id'],
|
'move_id': vals['move_id'],
|
||||||
'name': tools.ustr(vals['name'] or '') + ' ' + tools.ustr(tax['name'] or ''),
|
'name': tools.ustr(vals['name'] or '') + ' ' + tools.ustr(tax['name'] or ''),
|
||||||
|
|
|
@ -203,7 +203,7 @@ msgstr ""
|
||||||
#. module: account
|
#. module: account
|
||||||
#: help:account.tax.code,notprintable:0
|
#: help:account.tax.code,notprintable:0
|
||||||
#: help:account.tax.code.template,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 ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
|
@ -1853,7 +1853,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: report:account.journal.period.print.sale.purchase:0
|
#: report:account.journal.period.print.sale.purchase:0
|
||||||
msgid "VAT Declaration"
|
msgid "Tax Declaration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
|
@ -2249,7 +2249,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: view:account.vat.declaration:0
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
|
@ -4480,7 +4480,7 @@ msgstr ""
|
||||||
#. module: account
|
#. module: account
|
||||||
#: model:ir.actions.act_window,name:account.action_account_vat_declaration
|
#: model:ir.actions.act_window,name:account.action_account_vat_declaration
|
||||||
#: model:ir.model,name:account.model_account_vat_declaration
|
#: model:ir.model,name:account.model_account_vat_declaration
|
||||||
msgid "Account Vat Declaration"
|
msgid "Account Tax Declaration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
|
@ -4821,7 +4821,7 @@ msgstr ""
|
||||||
#: help:account.tax.template,ref_base_code_id:0
|
#: help:account.tax.template,ref_base_code_id:0
|
||||||
#: help:account.tax.template,ref_tax_code_id:0
|
#: help:account.tax.template,ref_tax_code_id:0
|
||||||
#: help:account.tax.template,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 ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
|
@ -5247,7 +5247,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: report:account.journal.period.print.sale.purchase:0
|
#: report:account.journal.period.print.sale.purchase:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
|
@ -7561,7 +7561,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: model:ir.actions.act_window,help:account.action_account_vat_declaration
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
|
|
|
@ -78,18 +78,19 @@
|
||||||
<page string="Accounting" col="4">
|
<page string="Accounting" col="4">
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="property_account_receivable" groups="account.group_account_invoice" />
|
|
||||||
<field name="property_account_position" widget="selection"/>
|
<field name="property_account_position" widget="selection"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<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="property_payment_term" widget="selection"/>
|
||||||
</group>
|
|
||||||
<group>
|
|
||||||
<field name="property_account_payable" groups="account.group_account_invoice"/>
|
|
||||||
</group>
|
|
||||||
<group>
|
|
||||||
<field name="credit"/>
|
<field name="credit"/>
|
||||||
<field name="credit_limit"/>
|
<field name="credit_limit"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
|
<field name="property_account_payable" groups="account.group_account_invoice"/>
|
||||||
<field name="debit"/>
|
<field name="debit"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
@ -142,16 +143,5 @@
|
||||||
view_type="form"
|
view_type="form"
|
||||||
view_mode="tree,form,graph,calendar"/>
|
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>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -219,7 +219,7 @@
|
||||||
<td><para style="P10a">Account</para></td>
|
<td><para style="P10a">Account</para></td>
|
||||||
<td><para style="P10a">Partner</para></td>
|
<td><para style="P10a">Partner</para></td>
|
||||||
<td><para style="P10a">Label</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="P11"></para></td>
|
||||||
<td><para style="P10b">Debit</para></td>
|
<td><para style="P10b">Debit</para></td>
|
||||||
<td><para style="P10b">Credit</para></td>
|
<td><para style="P10b">Credit</para></td>
|
||||||
|
@ -271,7 +271,7 @@
|
||||||
<td><para style="P10a">Account</para></td>
|
<td><para style="P10a">Account</para></td>
|
||||||
<td><para style="P10a">Partner</para></td>
|
<td><para style="P10a">Partner</para></td>
|
||||||
<td><para style="P10a">Label</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="P11"></para></td>
|
||||||
<td><para style="P10b">Debit</para></td>
|
<td><para style="P10b">Debit</para></td>
|
||||||
<td><para style="P10b">Credit</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">
|
<blockTable colWidths="15.0,80.0,20.0,182.0" style="Table1" repeatRows="1">
|
||||||
<tr>
|
<tr>
|
||||||
<td><para style="P12"></para></td>
|
<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>
|
||||||
<td><para style="P12"></para></td>
|
<td><para style="P12"></para></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
<record id="view_account_vat_declaration" model="ir.ui.view">
|
<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="model">account.vat.declaration</field>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Taxes Report" version="7.0">
|
<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">
|
<group string="Taxes Report" col="4">
|
||||||
<field name="chart_tax_id" widget='selection'/>
|
<field name="chart_tax_id" widget='selection'/>
|
||||||
<field name="fiscalyear_id"/>
|
<field name="fiscalyear_id"/>
|
||||||
|
@ -30,13 +30,13 @@
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_account_vat_declaration" model="ir.actions.act_window">
|
<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="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">account.vat.declaration</field>
|
<field name="res_model">account.vat.declaration</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">form</field>
|
<field name="view_mode">form</field>
|
||||||
<field name="target">new</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>
|
</record>
|
||||||
|
|
||||||
<menuitem
|
<menuitem
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"author" : "OpenERP SA",
|
"author" : "OpenERP SA",
|
||||||
"category": 'Accounting & Finance',
|
"category": 'Accounting & Finance',
|
||||||
"sequence": 10,
|
"sequence": 10,
|
||||||
|
"summary": "Accounts Moves, Journals, Chart of Accounts",
|
||||||
"description": """
|
"description": """
|
||||||
Accounting Access Rights.
|
Accounting Access Rights.
|
||||||
=========================
|
=========================
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
"name" : "eInvoicing & Payments",
|
"name" : "eInvoicing & Payments",
|
||||||
"version" : "1.0",
|
"version" : "1.0",
|
||||||
"author" : 'OpenERP SA',
|
"author" : 'OpenERP SA',
|
||||||
|
"summary": 'Supplier & Customer Invoices, Payments',
|
||||||
"description": """
|
"description": """
|
||||||
Account Voucher module includes all the basic requirements of Voucher Entries for Bank, Cash, Sales, Purchase, Expanse, Contra, etc.
|
Account Voucher module includes all the basic requirements of Voucher Entries for Bank, Cash, Sales, Purchase, Expanse, Contra, etc.
|
||||||
====================================================================================================================================
|
====================================================================================================================================
|
||||||
|
|
|
@ -39,9 +39,10 @@ If you need to manage your meetings, you should install the CRM module.
|
||||||
'category': 'Hidden/Dependency',
|
'category': 'Hidden/Dependency',
|
||||||
'website': 'http://www.openerp.com',
|
'website': 'http://www.openerp.com',
|
||||||
"init_xml": [
|
"init_xml": [
|
||||||
'base_calendar_data.xml'
|
'base_calendar_data.xml',
|
||||||
|
'crm_meeting_data.xml',
|
||||||
],
|
],
|
||||||
"demo_xml": [],
|
"demo_xml": ['crm_meeting_demo.xml'],
|
||||||
"update_xml": [
|
"update_xml": [
|
||||||
'security/calendar_security.xml',
|
'security/calendar_security.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
|
|
|
@ -515,7 +515,7 @@ property or property parameter."),
|
||||||
sub,
|
sub,
|
||||||
body,
|
body,
|
||||||
attachments=attach and {'invitation.ics': attach} or None,
|
attachments=attach and {'invitation.ics': attach} or None,
|
||||||
subtype='html',
|
content_subtype='html',
|
||||||
reply_to=email_from,
|
reply_to=email_from,
|
||||||
context=context
|
context=context
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<openerp>
|
<openerp>
|
||||||
<data noupdate="1">
|
<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="name">Event</field>
|
||||||
<field name="object">calendar.event</field>
|
<field name="object">calendar.event</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<field eval=""Meeting to discuss project plan and hash out the details of implementation "" name="description"/>
|
<field eval=""Meeting to discuss project plan and hash out the details of implementation "" name="description"/>
|
||||||
<field eval=""open"" name="state"/>
|
<field eval=""open"" name="state"/>
|
||||||
<field eval="time.strftime('%Y-%m-03 10:20:03')" name="date"/>
|
<field eval="time.strftime('%Y-%m-03 10:20:03')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet2"/>
|
<field name="categ_ids" eval="[(6,0,[ref('categ_meet2')])]"/>
|
||||||
<field eval=""Follow-up on proposal"" name="name"/>
|
<field eval=""Follow-up on proposal"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-03 16:38:03')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-03 16:38:03')" name="date_deadline"/>
|
||||||
<field eval="6.3" name="duration"/>
|
<field eval="6.3" name="duration"/>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field eval=""draft"" name="state"/>
|
<field eval=""draft"" name="state"/>
|
||||||
<field eval="time.strftime('%Y-%m-05 12:01:01')" name="date"/>
|
<field eval="time.strftime('%Y-%m-05 12:01:01')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet3"/>
|
<field name="categ_ids" eval="[(6,0,[ref('categ_meet3')])]"/>
|
||||||
<field eval=""Initial discussion"" name="name"/>
|
<field eval=""Initial discussion"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-05 19:01:01')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-05 19:01:01')" name="date_deadline"/>
|
||||||
<field eval=""contact@tecsas.fr"" name="email_from"/>
|
<field eval=""contact@tecsas.fr"" name="email_from"/>
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
<field eval=""Meeting to discuss project plan and hash out the details of implementation "" name="description"/>
|
<field eval=""Meeting to discuss project plan and hash out the details of implementation "" name="description"/>
|
||||||
<field eval=""done"" name="state"/>
|
<field eval=""done"" name="state"/>
|
||||||
<field eval="time.strftime('%Y-%m-12 15:55:05')" name="date"/>
|
<field eval="time.strftime('%Y-%m-12 15:55:05')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet1"/>
|
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1')])]"/>
|
||||||
<field eval=""Discuss pricing"" name="name"/>
|
<field eval=""Discuss pricing"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-12 18:55:05')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-12 18:55:05')" name="date_deadline"/>
|
||||||
</record>
|
</record>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
<field name="user_id" ref="base.user_demo"/>
|
<field name="user_id" ref="base.user_demo"/>
|
||||||
<field eval=""open"" name="state"/>
|
<field eval=""open"" name="state"/>
|
||||||
<field eval="time.strftime('%Y-%m-20 10:02:02')" name="date"/>
|
<field eval="time.strftime('%Y-%m-20 10:02:02')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet3"/>
|
<field name="categ_ids" eval="[(6,0,[ref('categ_meet3')])]"/>
|
||||||
<field eval=""Review needs"" name="name"/>
|
<field eval=""Review needs"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-20 16:02:02')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-20 16:02:02')" name="date_deadline"/>
|
||||||
</record>
|
</record>
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
<field name="user_id" ref="base.user_demo"/>
|
<field name="user_id" ref="base.user_demo"/>
|
||||||
<field eval=""draft"" name="state"/>
|
<field eval=""draft"" name="state"/>
|
||||||
<field eval="time.strftime('%Y-%m-22 11:05:05')" name="date"/>
|
<field eval="time.strftime('%Y-%m-22 11:05:05')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet2"/>
|
<field name="categ_ids" eval="[(6,0,[ref('categ_meet2')])]"/>
|
||||||
<field eval=""Changes in Designing"" name="name"/>
|
<field eval=""Changes in Designing"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-22 16:05:05')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-22 16:05:05')" name="date_deadline"/>
|
||||||
<field eval=""info@opensides.be"" name="email_from"/>
|
<field eval=""info@opensides.be"" name="email_from"/>
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field eval=""done"" name="state"/>
|
<field eval=""done"" name="state"/>
|
||||||
<field eval="time.strftime('%Y-%m-18 13:12:49')" name="date"/>
|
<field eval="time.strftime('%Y-%m-18 13:12:49')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet2"/>
|
<field name="categ_ids" eval="[(6,0,[ref('categ_meet2')])]"/>
|
||||||
<field eval=""Update the data"" name="name"/>
|
<field eval=""Update the data"" name="name"/>
|
||||||
<field eval="13.3" name="duration"/>
|
<field eval="13.3" name="duration"/>
|
||||||
<field eval="time.strftime('%Y-%m-19 02:30:49')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-19 02:30:49')" name="date_deadline"/>
|
|
@ -78,6 +78,7 @@ send an Email to Invited Person')
|
||||||
att_obj = self.pool.get('calendar.attendee')
|
att_obj = self.pool.get('calendar.attendee')
|
||||||
user_obj = self.pool.get('res.users')
|
user_obj = self.pool.get('res.users')
|
||||||
current_user = user_obj.browse(cr, uid, uid, context=context)
|
current_user = user_obj.browse(cr, uid, uid, context=context)
|
||||||
|
|
||||||
for datas in self.read(cr, uid, ids, context=context):
|
for datas in self.read(cr, uid, ids, context=context):
|
||||||
type = datas.get('type')
|
type = datas.get('type')
|
||||||
vals = []
|
vals = []
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<field name="model">res.partner</field>
|
<field name="model">res.partner</field>
|
||||||
<field name="inherit_id" ref="account.view_partner_property_form"/>
|
<field name="inherit_id" ref="account.view_partner_property_form"/>
|
||||||
<field name="arch" type="xml">
|
<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"/>
|
<label for="vat"/>
|
||||||
<div>
|
<div>
|
||||||
<field name="vat" on_change="vat_change(vat)" placeholder="BE0477472702" class="oe_inline"/>
|
<field name="vat" on_change="vat_change(vat)" placeholder="BE0477472702" class="oe_inline"/>
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
'version': '1.0',
|
'version': '1.0',
|
||||||
'category': 'Customer Relationship Management',
|
'category': 'Customer Relationship Management',
|
||||||
"sequence": 2,
|
"sequence": 2,
|
||||||
|
"summary": "Agenda, Leads, Opportunities",
|
||||||
'description': """
|
'description': """
|
||||||
The generic OpenERP Customer Relationship Management.
|
The generic OpenERP Customer Relationship Management.
|
||||||
=====================================================
|
=====================================================
|
||||||
|
@ -68,7 +69,6 @@ Creates a dashboard for CRM that includes:
|
||||||
'init_xml': [
|
'init_xml': [
|
||||||
'crm_data.xml',
|
'crm_data.xml',
|
||||||
'crm_lead_data.xml',
|
'crm_lead_data.xml',
|
||||||
'crm_meeting_data.xml',
|
|
||||||
'crm_phonecall_data.xml',
|
'crm_phonecall_data.xml',
|
||||||
],
|
],
|
||||||
'update_xml': [
|
'update_xml': [
|
||||||
|
@ -85,7 +85,6 @@ Creates a dashboard for CRM that includes:
|
||||||
'wizard/crm_opportunity_to_phonecall_view.xml',
|
'wizard/crm_opportunity_to_phonecall_view.xml',
|
||||||
'wizard/crm_partner_to_opportunity_view.xml',
|
'wizard/crm_partner_to_opportunity_view.xml',
|
||||||
|
|
||||||
'wizard/crm_add_note_view.xml',
|
|
||||||
'wizard/crm_merge_opportunities_view.xml',
|
'wizard/crm_merge_opportunities_view.xml',
|
||||||
|
|
||||||
'crm_view.xml',
|
'crm_view.xml',
|
||||||
|
@ -115,7 +114,6 @@ Creates a dashboard for CRM that includes:
|
||||||
'demo_xml': [
|
'demo_xml': [
|
||||||
'crm_demo.xml',
|
'crm_demo.xml',
|
||||||
'crm_lead_demo.xml',
|
'crm_lead_demo.xml',
|
||||||
'crm_meeting_demo.xml',
|
|
||||||
'crm_phonecall_demo.xml',
|
'crm_phonecall_demo.xml',
|
||||||
],
|
],
|
||||||
'test': [
|
'test': [
|
||||||
|
|
|
@ -2,10 +2,16 @@
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<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"/>
|
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"/>
|
parent="base.menu_base_config" sequence="80" groups="base.group_sale_manager"/>
|
||||||
|
|
||||||
<menuitem id="base.menu_crm_config_opportunity" name="Opportunities"
|
<menuitem id="base.menu_crm_config_opportunity" name="Opportunities"
|
||||||
|
|
|
@ -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_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_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
|
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_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_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,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
|
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_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_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_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
|
access_crm_lead_report_user,crm.lead.report user,model_crm_lead_report,base.group_sale_salesman,1,1,1,1
|
||||||
|
|
|
|
@ -54,10 +54,8 @@
|
||||||
-
|
-
|
||||||
After communicated with customer, I put some notes with contract details.
|
After communicated with customer, I put some notes with contract details.
|
||||||
-
|
-
|
||||||
!python {model: crm.add.note}: |
|
!python {model: crm.lead}: |
|
||||||
context.update({'active_model': 'crm.lead', 'active_id': ref('crm_case_qrecorp0')})
|
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é')
|
||||||
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)
|
|
||||||
-
|
-
|
||||||
I win this opportunity
|
I win this opportunity
|
||||||
-
|
-
|
||||||
|
@ -113,7 +111,7 @@
|
||||||
-
|
-
|
||||||
!python {model: crm.meeting}: |
|
!python {model: crm.meeting}: |
|
||||||
context.update({'active_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.
|
I invite a user for meeting.
|
||||||
-
|
-
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
import crm_add_note
|
|
||||||
|
|
||||||
import crm_lead_to_partner
|
import crm_lead_to_partner
|
||||||
import crm_lead_to_opportunity
|
import crm_lead_to_opportunity
|
||||||
import crm_phonecall_to_phonecall
|
import crm_phonecall_to_phonecall
|
||||||
|
|
|
@ -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:
|
|
|
@ -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>
|
|
||||||
|
|
|
@ -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 ""
|
|
@ -101,6 +101,11 @@ class email_template(osv.osv):
|
||||||
mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model
|
mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model
|
||||||
return {'value':{'model': mod_name}}
|
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 = {
|
_columns = {
|
||||||
'name': fields.char('Name', size=250),
|
'name': fields.char('Name', size=250),
|
||||||
'model_id': fields.many2one('ir.model', 'Related document model'),
|
'model_id': fields.many2one('ir.model', 'Related document model'),
|
||||||
|
@ -311,7 +316,7 @@ class email_template(osv.osv):
|
||||||
'attachment_ids': False,
|
'attachment_ids': False,
|
||||||
'message_id': False,
|
'message_id': False,
|
||||||
'state': 'outgoing',
|
'state': 'outgoing',
|
||||||
'subtype': 'plain',
|
'content_subtype': 'plain',
|
||||||
}
|
}
|
||||||
if not template_id:
|
if not template_id:
|
||||||
return values
|
return values
|
||||||
|
@ -326,8 +331,15 @@ class email_template(osv.osv):
|
||||||
template.model, res_id, context=context) \
|
template.model, res_id, context=context) \
|
||||||
or False
|
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']:
|
if values['body_html']:
|
||||||
values.update(subtype='html')
|
values.update(content_subtype='html')
|
||||||
|
|
||||||
if template.user_signature:
|
if template.user_signature:
|
||||||
signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
|
signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
|
||||||
|
|
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 3.0 KiB |
|
@ -25,5 +25,32 @@
|
||||||
</data>
|
</data>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</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>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -27,19 +27,6 @@ from tools.translate import _
|
||||||
import tools
|
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):
|
class mail_compose_message(osv.osv_memory):
|
||||||
_inherit = 'mail.compose.message'
|
_inherit = 'mail.compose.message'
|
||||||
|
|
||||||
|
@ -87,6 +74,8 @@ class mail_compose_message(osv.osv_memory):
|
||||||
else:
|
else:
|
||||||
# render the mail as one-shot
|
# render the mail as one-shot
|
||||||
values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context)
|
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
|
# retrofit generated attachments in the expected field format
|
||||||
if values['attachments']:
|
if values['attachments']:
|
||||||
attachment = values.pop('attachments')
|
attachment = values.pop('attachments')
|
||||||
|
@ -110,18 +99,24 @@ class mail_compose_message(osv.osv_memory):
|
||||||
|
|
||||||
return {'value': values}
|
return {'value': values}
|
||||||
|
|
||||||
|
|
||||||
def template_toggle(self, cr, uid, ids, context=None):
|
def template_toggle(self, cr, uid, ids, context=None):
|
||||||
for record in self.browse(cr, uid, ids, context=context):
|
for record in self.browse(cr, uid, ids, context=context):
|
||||||
had_template = record.use_template
|
values = {}
|
||||||
record.write({'use_template': not(had_template)})
|
use_template = record.use_template
|
||||||
if had_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
|
# equivalent to choosing an empty template
|
||||||
onchange_defaults = self.on_change_template(cr, uid, record.id, not(had_template),
|
onchange_template_values = self.on_change_template(cr, uid, record.id, use_template,
|
||||||
False, email_from=record.email_from,
|
False, email_from=record.email_from, email_to=record.email_to, context=context)
|
||||||
email_to=record.email_to, context=context)
|
values.update(onchange_template_values['value'])
|
||||||
record.write(onchange_defaults['value'])
|
return {'value': values}
|
||||||
return _reopen(self, record.id, record.model)
|
|
||||||
|
|
||||||
def save_as_template(self, cr, uid, ids, context=None):
|
def save_as_template(self, cr, uid, ids, context=None):
|
||||||
if context is None:
|
if context is None:
|
||||||
|
@ -153,7 +148,7 @@ class mail_compose_message(osv.osv_memory):
|
||||||
'use_template': True})
|
'use_template': True})
|
||||||
|
|
||||||
# _reopen same wizard screen with new template preselected
|
# _reopen same wizard screen with new template preselected
|
||||||
return _reopen(self, record.id, model)
|
return False
|
||||||
|
|
||||||
# override the basic implementation
|
# override the basic implementation
|
||||||
def render_template(self, cr, uid, template, model, res_id, context=None):
|
def render_template(self, cr, uid, template, model, res_id, context=None):
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
'name': 'Events Organisation',
|
'name': 'Events Organisation',
|
||||||
'version': '0.1',
|
'version': '0.1',
|
||||||
'category': 'Tools',
|
'category': 'Tools',
|
||||||
|
'summary': 'Invitations, Subscriptions',
|
||||||
'description': """
|
'description': """
|
||||||
Organization and management of Events.
|
Organization and management of Events.
|
||||||
======================================
|
======================================
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
"category": "Human Resources",
|
"category": "Human Resources",
|
||||||
"sequence": 12,
|
"sequence": 12,
|
||||||
"website": "http://www.openerp.com",
|
"website": "http://www.openerp.com",
|
||||||
|
"summary": "Hierarchy, Jobs, Departments",
|
||||||
"description": """
|
"description": """
|
||||||
Module for human resource management.
|
Module for human resource management.
|
||||||
=====================================
|
=====================================
|
||||||
|
|
|
@ -212,6 +212,16 @@ class hr_employee(osv.osv):
|
||||||
'last_login': fields.related('user_id', 'date', type='datetime', string='Latest Connection', readonly=1),
|
'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):
|
def unlink(self, cr, uid, ids, context=None):
|
||||||
resource_obj = self.pool.get('resource.resource')
|
resource_obj = self.pool.get('resource.resource')
|
||||||
resource_ids = []
|
resource_ids = []
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"author": "OpenERP SA",
|
"author": "OpenERP SA",
|
||||||
"category": "Human Resources",
|
"category": "Human Resources",
|
||||||
"website": "http://www.openerp.com",
|
"website": "http://www.openerp.com",
|
||||||
|
"summary": "Top-Down and Bottom-Up Evaluation",
|
||||||
"images": ["images/hr_evaluation_analysis.jpeg","images/hr_evaluation.jpeg"],
|
"images": ["images/hr_evaluation_analysis.jpeg","images/hr_evaluation.jpeg"],
|
||||||
"depends": ["hr","base_calendar","survey"],
|
"depends": ["hr","base_calendar","survey"],
|
||||||
"description": """
|
"description": """
|
||||||
|
|
|
@ -44,15 +44,9 @@ class mail_compose_message(osv.osv_memory):
|
||||||
if record_data.state == "waiting_answer":
|
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)
|
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({
|
result.update({
|
||||||
'email_from': tools.config.get('email_from',''),
|
|
||||||
'email_to': record_data.user_to_review_id.work_email or False,
|
'email_to': record_data.user_to_review_id.work_email or False,
|
||||||
'subject': _("Reminder to fill up Survey"),
|
'subject': _("Reminder to fill up Survey"),
|
||||||
'body_text': msg,
|
'body_text': msg,
|
||||||
'res_id': resource_id,
|
|
||||||
'model': model,
|
|
||||||
'email_cc': False,
|
|
||||||
'email_bcc': False,
|
|
||||||
'reply_to': False,
|
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
'version': '1.0',
|
'version': '1.0',
|
||||||
'category': 'Human Resources',
|
'category': 'Human Resources',
|
||||||
"sequence": 30,
|
"sequence": 30,
|
||||||
|
"summary": "Expenses Validation, Invoicing",
|
||||||
'description': """
|
'description': """
|
||||||
This module aims to manage employee's expenses.
|
This module aims to manage employee's expenses.
|
||||||
===============================================
|
===============================================
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
"author": "OpenERP SA",
|
"author": "OpenERP SA",
|
||||||
"category": "Human Resources",
|
"category": "Human Resources",
|
||||||
"sequence": 28,
|
"sequence": 28,
|
||||||
|
"summary": "Allocation and Leave Requests, Reporting by Department",
|
||||||
"website": "http://www.openerp.com",
|
"website": "http://www.openerp.com",
|
||||||
"description": """
|
"description": """
|
||||||
This module allows you to manage leaves and leaves' requests.
|
This module allows you to manage leaves and leaves' requests.
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
'version': '1.0',
|
'version': '1.0',
|
||||||
'category': 'Human Resources',
|
'category': 'Human Resources',
|
||||||
"sequence": 24,
|
"sequence": 24,
|
||||||
|
"summary": "Job Descriptions, Recruitment Process",
|
||||||
'description': """
|
'description': """
|
||||||
Manages job positions and the recruitment process.
|
Manages job positions and the recruitment process.
|
||||||
==================================================
|
==================================================
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<openerp>
|
<openerp>
|
||||||
<!--
|
|
||||||
<data noupdate="1">
|
<data noupdate="1">
|
||||||
-->
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<!-- Meeting Types (for interview meetings) -->
|
<!-- Meeting Types (for interview meetings) -->
|
||||||
<record model="crm.meeting.type" id="categ_meet_interview">
|
<record model="crm.meeting.type" id="categ_meet_interview">
|
||||||
|
|
|
@ -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_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_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_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_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_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
|
access_crm_meeting_hruser,crm.meeting.hruser,base_calendar.model_crm_meeting,base.group_hr_user,1,1,1,1
|
||||||
|
|
|
|
@ -25,6 +25,7 @@
|
||||||
'version': '1.0',
|
'version': '1.0',
|
||||||
'category': 'Human Resources',
|
'category': 'Human Resources',
|
||||||
"sequence": 16,
|
"sequence": 16,
|
||||||
|
"summary": "Attendances, Activities, Timing",
|
||||||
'description': """
|
'description': """
|
||||||
This module helps you to easily encode and validate timesheet and attendances within the same view.
|
This module helps you to easily encode and validate timesheet and attendances within the same view.
|
||||||
===================================================================================================
|
===================================================================================================
|
||||||
|
|
|
@ -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 ""
|
|
@ -9,7 +9,7 @@
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
<field name="arch" type="xml">
|
<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_type"/>
|
||||||
<field name="out_inv_comm_algorithm" attrs="{'invisible':[('out_inv_comm_type','!=','bba')]}"/>
|
<field name="out_inv_comm_algorithm" attrs="{'invisible':[('out_inv_comm_type','!=','bba')]}"/>
|
||||||
</field>
|
</field>
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
'version': '1.0',
|
'version': '1.0',
|
||||||
'category':'Social Network',
|
'category':'Social Network',
|
||||||
"sequence": 2,
|
"sequence": 2,
|
||||||
|
"summary": "Discussions, Feeds, Alerts",
|
||||||
'description': """
|
'description': """
|
||||||
A bussiness oriented Social Networking with a fully-integrated email
|
A bussiness oriented Social Networking with a fully-integrated email
|
||||||
and message management.
|
and message management.
|
||||||
|
@ -66,11 +67,11 @@ The main features of the module are :
|
||||||
'mail_thread_view.xml',
|
'mail_thread_view.xml',
|
||||||
'mail_group_view.xml',
|
'mail_group_view.xml',
|
||||||
'res_partner_view.xml',
|
'res_partner_view.xml',
|
||||||
|
'res_users_view.xml',
|
||||||
|
'data/mail_data.xml',
|
||||||
|
'data/mail_group_data.xml',
|
||||||
'security/mail_security.xml',
|
'security/mail_security.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'mail_data.xml',
|
|
||||||
'mail_group_data.xml',
|
|
||||||
'res_users_view.xml',
|
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'auto_install': False,
|
'auto_install': False,
|
||||||
|
@ -83,13 +84,15 @@ The main features of the module are :
|
||||||
'static/src/img/email_icong.png',
|
'static/src/img/email_icong.png',
|
||||||
'static/src/img/_al.png',
|
'static/src/img/_al.png',
|
||||||
'static/src/img/_pincky.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/groupdefault.png',
|
||||||
|
'static/src/img/attachment.png',
|
||||||
|
'static/src/img/checklist.png',
|
||||||
|
'static/src/img/formatting.png',
|
||||||
],
|
],
|
||||||
'css': [
|
'css': [
|
||||||
'static/src/css/mail.css',
|
'static/src/css/mail.css',
|
||||||
'static/src/css/mail_group.css',
|
'static/src/css/mail_group.css',
|
||||||
|
'static/src/css/mail_compose_message.css',
|
||||||
],
|
],
|
||||||
'js': [
|
'js': [
|
||||||
'static/lib/jquery.expander/jquery.expander.js',
|
'static/lib/jquery.expander/jquery.expander.js',
|
||||||
|
@ -99,7 +102,7 @@ The main features of the module are :
|
||||||
'static/src/xml/mail.xml',
|
'static/src/xml/mail.xml',
|
||||||
],
|
],
|
||||||
'demo': [
|
'demo': [
|
||||||
'mail_demo.xml',
|
'data/mail_demo.xml',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data noupdate="1">
|
||||||
|
|
||||||
<record id="message_blogpost0_attachment0" model="ir.attachment">
|
<record id="message_blogpost0_attachment0" model="ir.attachment">
|
||||||
<field name="name">A cool attachment</field>
|
<field name="name">A cool attachment</field>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
<field name="subject">Internal company announce</field>
|
<field name="subject">Internal company announce</field>
|
||||||
<field name="model">mail.group</field>
|
<field name="model">mail.group</field>
|
||||||
<field name="res_id" ref="group_all_company"/>
|
<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.
|
<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.
|
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>
|
||||||
|
|
||||||
<record id="message_blogpost0_comment0" model="mail.message">
|
<record id="message_blogpost0_comment0" model="mail.message">
|
||||||
<field name="subject">Reply</field>
|
|
||||||
<field name="model">mail.group</field>
|
<field name="model">mail.group</field>
|
||||||
<field name="res_id" ref="group_all_company"/>
|
<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="body_html"><![CDATA[That was such a <b>tremendous</b> blogpost ! (first comment)]]></field>
|
||||||
<field name="parent_id" ref="message_blogpost0"/>
|
<field name="parent_id" ref="message_blogpost0"/>
|
||||||
<field name="type">comment</field>
|
<field name="type">comment</field>
|
||||||
|
@ -52,10 +51,9 @@ Nulla turpis leo, rhoncus ut egestas sit amet, consectetur vitae urna. Mauris in
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="message_blogpost0_comment1" model="mail.message">
|
<record id="message_blogpost0_comment1" model="mail.message">
|
||||||
<field name="subject">Reply</field>
|
|
||||||
<field name="model">mail.group</field>
|
<field name="model">mail.group</field>
|
||||||
<field name="res_id" ref="group_all_company"/>
|
<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 !
|
<field name="body_html"><![CDATA[Agreed !
|
||||||
Would it be possible to learn more about the author ? (second comment)]]></field>
|
Would it be possible to learn more about the author ? (second comment)]]></field>
|
||||||
<field name="parent_id" ref="message_blogpost0"/>
|
<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>
|
||||||
|
|
||||||
<record id="message_blogpost0_comment2" model="mail.message">
|
<record id="message_blogpost0_comment2" model="mail.message">
|
||||||
<field name="subject">Reply</field>
|
|
||||||
<field name="model">mail.group</field>
|
<field name="model">mail.group</field>
|
||||||
<field name="res_id" ref="group_all_company"/>
|
<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.
|
<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.
|
In quis purus neque. Integer sodales dolor eu elit fringilla blandit. Maecenas lacus nisi, facilisis sit amet viverra eu, tristique vel augue.
|
|
@ -4,10 +4,15 @@
|
||||||
|
|
||||||
<record model="mail.group" id="group_all_company">
|
<record model="mail.group" id="group_all_company">
|
||||||
<field name="name">All Company</field>
|
<field name="name">All Company</field>
|
||||||
|
<field name="description">All company users can come here and discuss.</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="mail.group" id="group_sales">
|
<record model="mail.group" id="group_sales">
|
||||||
<field name="name">Sales</field>
|
<field name="name">Sales</field>
|
||||||
</record>
|
</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>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
|
@ -3,22 +3,26 @@
|
||||||
mail.message
|
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.
|
All internal logic should be in a database-based model while this model
|
||||||
Additional information on fields:
|
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:
|
- ``subtype`` is renamed to ``content_subtype``: usually 'html' or 'plain'.
|
||||||
- it adds a preference about sending emails when receiving a notification
|
This field is used to select plain-text or rich-text contents accordingly.
|
||||||
- make a new user follow itself automatically
|
- ``subtype`` is moved to mail.message model. The purpose is to be able to
|
||||||
- create a welcome message when creating a new user, to make his arrival in OpenERP more friendly
|
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.
|
||||||
|
|
|
@ -3,7 +3,25 @@
|
||||||
mail.thread and OpenChatter
|
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
|
mail.group
|
||||||
++++++++++
|
++++++++++
|
||||||
|
|
|
@ -36,7 +36,7 @@ class mail_group(osv.osv):
|
||||||
A mail_group is a collection of users sharing messages in a discussion
|
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
|
group. Group users are users that follow the mail group, using the
|
||||||
subscription/follow mechanism of OpenSocial. A mail group has nothing
|
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:
|
Additional information on fields:
|
||||||
- ``member_ids``: user member of the groups are calculated with
|
- ``member_ids``: user member of the groups are calculated with
|
||||||
``message_get_subscribers`` method from mail.thread
|
``message_get_subscribers`` method from mail.thread
|
||||||
|
@ -49,12 +49,6 @@ class mail_group(osv.osv):
|
||||||
_name = 'mail.group'
|
_name = 'mail.group'
|
||||||
_inherit = ['mail.thread']
|
_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):
|
def onchange_photo(self, cr, uid, ids, value, context=None):
|
||||||
if not value:
|
if not value:
|
||||||
return {'value': {'avatar_big': value, 'avatar': value} }
|
return {'value': {'avatar_big': value, 'avatar': value} }
|
||||||
|
@ -105,7 +99,7 @@ class mail_group(osv.osv):
|
||||||
message_obj = self.pool.get('mail.message')
|
message_obj = self.pool.get('mail.message')
|
||||||
for id in ids:
|
for id in ids:
|
||||||
lower_date = (DT.datetime.now() - DT.timedelta(days=30)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
|
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
|
return result
|
||||||
|
|
||||||
def _get_default_photo(self, cr, uid, context=None):
|
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),
|
'name': fields.char('Name', size=64, required=True),
|
||||||
'description': fields.text('Description'),
|
'description': fields.text('Description'),
|
||||||
'responsible_id': fields.many2one('res.users', string='Responsible',
|
'responsible_id': fields.many2one('res.users', string='Responsible',
|
||||||
ondelete='set null', required=True, select=1,
|
ondelete='set null', required=True, select=1,
|
||||||
help="Responsible of the group that has all rights on the record."),
|
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.'),
|
'public': fields.boolean('Visible by non members', help='This group is visible by non members. \
|
||||||
'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.'),
|
Invisible groups can add members through the invite button.'),
|
||||||
'photo': fields.function(_get_photo, fnct_inv=_set_photo, string='Photo', type="binary",
|
'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 = {
|
store = {
|
||||||
'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['photo_big'], 10),
|
'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',
|
help='Field holding the automatically resized (128x128) PIL-supported and base64 encoded version of the group image.'),
|
||||||
relation='res.users', string='Group members', multi='get_member_ids'),
|
'member_ids': fields.function(get_member_ids, fnct_search=search_member_ids,
|
||||||
'member_count': fields.function(get_member_ids, type='integer', string='Member count', multi='get_member_ids'),
|
type='many2many', relation='res.users', string='Group members', multi='get_member_ids'),
|
||||||
'is_subscriber': fields.function(get_member_ids, type='boolean', string='Joined', multi='get_member_ids'),
|
'member_count': fields.function(get_member_ids, type='integer',
|
||||||
'last_month_msg_nbr': fields.function(get_last_month_msg_nbr, type='integer', string='Messages count for last month'),
|
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 = {
|
_defaults = {
|
||||||
|
@ -136,3 +144,32 @@ class mail_group(osv.osv):
|
||||||
'responsible_id': (lambda s, cr, uid, ctx: uid),
|
'responsible_id': (lambda s, cr, uid, ctx: uid),
|
||||||
'photo': _get_default_photo,
|
'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)
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
<newline/>
|
<newline/>
|
||||||
<group colspan="4" col="4">
|
<group colspan="4" col="4">
|
||||||
<field name="description" colspan="4" nolabel="1"/>
|
<field name="description" colspan="4" nolabel="1"/>
|
||||||
|
<field name="group_ids" colspan="4" widget="many2many_tags" class="oe_edit_only"/>
|
||||||
</group>
|
</group>
|
||||||
<group colspan="2" col="2" class="oe_edit_only">
|
<group colspan="2" col="2" class="oe_edit_only">
|
||||||
<field name="responsible_id" colspan="2"/>
|
<field name="responsible_id" colspan="2"/>
|
||||||
|
|
|
@ -30,66 +30,61 @@ import datetime
|
||||||
from email.header import decode_header
|
from email.header import decode_header
|
||||||
from email.message import Message
|
from email.message import Message
|
||||||
|
|
||||||
import tools
|
from openerp import SUPERUSER_ID
|
||||||
from osv import osv
|
from osv import osv
|
||||||
from osv import fields
|
from osv import fields
|
||||||
|
import pytz
|
||||||
|
from tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||||
from tools.translate import _
|
from tools.translate import _
|
||||||
from openerp import SUPERUSER_ID
|
import tools
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def format_date_tz(date, tz=None):
|
""" Some tools for parsing / creating email fields """
|
||||||
if not date:
|
|
||||||
return 'n/a'
|
|
||||||
format = tools.DEFAULT_SERVER_DATETIME_FORMAT
|
|
||||||
return tools.server_to_local_timestamp(date, format, format, tz)
|
|
||||||
|
|
||||||
def truncate_text(text):
|
|
||||||
lines = text and text.split('\n') or []
|
|
||||||
if len(lines) > 3:
|
|
||||||
res = '\n\t'.join(lines[:3]) + '...'
|
|
||||||
else:
|
|
||||||
res = '\n\t'.join(lines)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def decode(text):
|
def decode(text):
|
||||||
"""Returns unicode() string conversion of the the given encoded smtp header text"""
|
"""Returns unicode() string conversion of the the given encoded smtp header text"""
|
||||||
if text:
|
if text:
|
||||||
text = decode_header(text.replace('\r', ''))
|
text = decode_header(text.replace('\r', ''))
|
||||||
return ''.join([tools.ustr(x[0], x[1]) for x in text])
|
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``"""
|
"""Return a list of the email addresses found in ``text``"""
|
||||||
if not text: return []
|
if not text: return []
|
||||||
return re.findall(r'([^ ,<@]+@[^> ,]+)', text)
|
return re.findall(r'([^ ,<@]+@[^> ,]+)', text)
|
||||||
|
|
||||||
class mail_message_common(osv.osv_memory):
|
# TODO: remove that after cleaning
|
||||||
"""Common abstract class for holding the main attributes of a
|
def to_email(text):
|
||||||
message object. It could be reused as parent model for any
|
return mail_tools_to_email(text)
|
||||||
database model or wizard screen that needs to hold a kind of
|
|
||||||
message"""
|
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):
|
def get_body(self, cr, uid, ids, name, arg, context=None):
|
||||||
if context is None:
|
""" get correct body version: body_html for html messages, and
|
||||||
context = {}
|
body_text for plain text messages
|
||||||
|
"""
|
||||||
result = dict.fromkeys(ids, '')
|
result = dict.fromkeys(ids, '')
|
||||||
for message in self.browse(cr, uid, ids, context=context):
|
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
|
result[message.id] = message.body_html
|
||||||
else:
|
else:
|
||||||
result[message.id] = message.body_text
|
result[message.id] = message.body_text
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def search_body(self, cr, uid, obj, name, args, context=None):
|
def search_body(self, cr, uid, obj, name, args, context=None):
|
||||||
"""will receive:
|
# will receive:
|
||||||
- obj: mail.message object
|
# - obj: mail.message object
|
||||||
- name: 'body'
|
# - name: 'body'
|
||||||
- args: [('body', 'ilike', 'blah')]"""
|
# - args: [('body', 'ilike', 'blah')]
|
||||||
return ['|', '&', ('subtype', '=', 'html'), ('body_html', args[0][1], args[0][2]), ('body_text', args[0][1], args[0][2])]
|
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):
|
def get_record_name(self, cr, uid, ids, name, arg, context=None):
|
||||||
if context is None:
|
|
||||||
context = {}
|
|
||||||
result = dict.fromkeys(ids, '')
|
result = dict.fromkeys(ids, '')
|
||||||
for message in self.browse(cr, uid, ids, context=context):
|
for message in self.browse(cr, uid, ids, context=context):
|
||||||
if not message.model or not message.res_id:
|
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),
|
'subject': fields.char('Subject', size=512),
|
||||||
'model': fields.char('Related Document Model', size=128, select=1),
|
'model': fields.char('Related Document Model', size=128, select=1),
|
||||||
'res_id': fields.integer('Related Document ID', select=1),
|
'res_id': fields.integer('Related Document ID', select=1),
|
||||||
'record_name': fields.function(get_record_name, type='string', string='Message Record Name',
|
'record_name': fields.function(get_record_name, type='string',
|
||||||
help="Name of the record, matching the result of the name_get."),
|
string='Message Record Name',
|
||||||
|
help="Name get of the related document."),
|
||||||
'date': fields.datetime('Date'),
|
'date': fields.datetime('Date'),
|
||||||
'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'),
|
'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'),
|
||||||
'email_to': fields.char('To', size=256, help='Message recipients'),
|
'email_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'),
|
'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'),
|
'reply_to':fields.char('Reply-To', size=256, help='Preferred response address for the message'),
|
||||||
'headers': fields.text('Message Headers', readonly=1,
|
'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),
|
'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),
|
'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 "
|
'content_subtype': fields.char('Message content subtype', size=32,
|
||||||
"select plaintext or rich text contents accordingly", readonly=1),
|
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_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_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',
|
'body': fields.function(get_body, fnct_search = search_body, 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."),
|
string='Message Content', store=True,
|
||||||
'parent_id': fields.many2one('mail.message', 'Parent Message', help="Parent message, used for displaying as threads with hierarchy",
|
help="Content of the message. This content equals the body_text field "\
|
||||||
select=True, ondelete='set null',),
|
"for plain-test messages, and body_html for rich-text/HTML "\
|
||||||
'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'),
|
"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 = {
|
_defaults = {
|
||||||
'subtype': 'plain',
|
'content_subtype': 'plain',
|
||||||
'date': (lambda *a: fields.datetime.now()),
|
'date': (lambda *a: fields.datetime.now()),
|
||||||
}
|
}
|
||||||
|
|
||||||
class mail_message(osv.osv):
|
class mail_message(osv.Model):
|
||||||
'''Model holding messages: system notification (replacing res.log
|
"""Model holding messages: system notification (replacing res.log
|
||||||
notifications), comments (for OpenSocial feature) and
|
notifications), comments (for OpenChatter feature) and
|
||||||
RFC2822 email messages. This model also provides facilities to
|
RFC2822 email messages. This model also provides facilities to
|
||||||
parse, queue and send new email messages. Type of messages
|
parse, queue and send new email messages. Type of messages
|
||||||
are differentiated using the 'type' column.
|
are differentiated using the 'type' column. """
|
||||||
|
|
||||||
The ``display_text`` field will have a slightly different
|
|
||||||
presentation for real emails and for log messages.
|
|
||||||
'''
|
|
||||||
|
|
||||||
_name = 'mail.message'
|
_name = 'mail.message'
|
||||||
_inherit = 'mail.message.common'
|
_inherit = 'mail.message.common'
|
||||||
_description = 'Mail Message (email, comment, notification)'
|
_description = 'Mail Message (email, comment, notification)'
|
||||||
_order = 'date desc'
|
_order = 'date desc'
|
||||||
|
|
||||||
# XXX to review - how to determine action to use?
|
|
||||||
def open_document(self, cr, uid, ids, context=None):
|
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
|
action_data = False
|
||||||
if ids:
|
if not ids:
|
||||||
msg = self.browse(cr, uid, ids[0], context=context)
|
return action_data
|
||||||
model = msg.model
|
msg = self.browse(cr, uid, ids[0], context=context)
|
||||||
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', '=', msg.model)], context=context)
|
||||||
ir_act_window = self.pool.get('ir.actions.act_window')
|
if action_ids:
|
||||||
action_ids = ir_act_window.search(cr, uid, [('res_model', '=', model)])
|
action_data = ir_act_window.read(cr, uid, action_ids[0], context=context)
|
||||||
if action_ids:
|
action_data.update({
|
||||||
action_data = ir_act_window.read(cr, uid, action_ids[0], context=context)
|
'domain' : "[('id', '=', %d)]" % (msg.res_id),
|
||||||
action_data.update({
|
|
||||||
'domain' : "[('id','=',%d)]"%(res_id),
|
|
||||||
'nodestroy': True,
|
'nodestroy': True,
|
||||||
'context': {}
|
'context': {}
|
||||||
})
|
})
|
||||||
return action_data
|
return action_data
|
||||||
|
|
||||||
# XXX to review - how to determine action to use?
|
|
||||||
def open_attachment(self, cr, uid, ids, context=None):
|
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
|
action_data = False
|
||||||
|
if not ids:
|
||||||
|
return action_data
|
||||||
action_pool = self.pool.get('ir.actions.act_window')
|
action_pool = self.pool.get('ir.actions.act_window')
|
||||||
message = self.browse(cr, uid, ids, context=context)[0]
|
messages = self.browse(cr, uid, ids, context=context)
|
||||||
att_ids = [x.id for x in message.attachment_ids]
|
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')])
|
action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')], context=context)
|
||||||
if action_ids:
|
if action_ids:
|
||||||
action_data = action_pool.read(cr, uid, action_ids[0], context=context)
|
action_data = action_pool.read(cr, uid, action_ids[0], context=context)
|
||||||
action_data.update({
|
action_data.update({
|
||||||
'domain': [('id','in',att_ids)],
|
'domain': [('id', 'in', att_ids)],
|
||||||
'nodestroy': True
|
'nodestroy': True
|
||||||
})
|
})
|
||||||
return action_data
|
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 = {
|
_columns = {
|
||||||
'type': fields.selection([
|
'type': fields.selection([
|
||||||
('email', 'email'),
|
('email', 'email'),
|
||||||
('comment', 'Comment'),
|
('comment', 'Comment'),
|
||||||
('notification', 'System notification'),
|
('notification', 'System notification'),
|
||||||
], 'Type', help="Message type: email for email message, notification for system message, comment for other messages such as user replies"),
|
], 'Type',
|
||||||
'partner_id': fields.many2one('res.partner', 'Related partner'),
|
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),
|
'user_id': fields.many2one('res.users', 'Related User', readonly=1),
|
||||||
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'),
|
'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),
|
'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1),
|
||||||
'state': fields.selection([
|
'state': fields.selection([
|
||||||
('outgoing', 'Outgoing'),
|
('outgoing', 'Outgoing'),
|
||||||
|
@ -231,8 +222,14 @@ class mail_message(osv.osv):
|
||||||
('exception', 'Delivery Failed'),
|
('exception', 'Delivery Failed'),
|
||||||
('cancel', 'Cancelled'),
|
('cancel', 'Cancelled'),
|
||||||
], 'Status', readonly=True),
|
], 'Status', readonly=True),
|
||||||
'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"),
|
'auto_delete': fields.boolean('Auto Delete',
|
||||||
'original': fields.binary('Original', help="Original version of the message, as it was sent on the network", readonly=1),
|
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 = {
|
_defaults = {
|
||||||
|
@ -249,72 +246,124 @@ class mail_message(osv.osv):
|
||||||
if not cr.fetchone():
|
if not cr.fetchone():
|
||||||
cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""")
|
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):
|
def copy(self, cr, uid, id, default=None, context=None):
|
||||||
"""Overridden to avoid duplicating fields that are unique to each email"""
|
"""Overridden to avoid duplicating fields that are unique to each email"""
|
||||||
if default is None:
|
if default is None:
|
||||||
default = {}
|
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)
|
return super(mail_message,self).copy(cr, uid, id, default=default, context=context)
|
||||||
|
|
||||||
|
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 schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, email_cc=None,
|
def unlink(self, cr, uid, ids, context=None):
|
||||||
email_bcc=None, reply_to=False, attachments=None, message_id=False, references=False,
|
self.check(cr, uid, ids, 'unlink', context=context)
|
||||||
res_id=False, subtype='plain', headers=None, mail_server_id=False, auto_delete=False,
|
return super(mail_message, self).unlink(cr, uid, ids, context)
|
||||||
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
|
def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, type='email',
|
||||||
:param list email_to: list of recipient addresses (to be joined with commas)
|
email_cc=None, email_bcc=None, reply_to=False, partner_ids=None, attachments=None,
|
||||||
:param string subject: email subject (no pre-encoding/quoting necessary)
|
message_id=False, references=False, res_id=False, content_subtype='plain',
|
||||||
:param string body: email body, according to the ``subtype`` (by default, plaintext).
|
headers=None, mail_server_id=False, auto_delete=False, context=None):
|
||||||
If html subtype is used, the message will be automatically converted
|
""" Schedule sending a new email message, to be sent the next time the
|
||||||
to plaintext and wrapped in multipart/alternative.
|
mail scheduler runs, or the next time :meth:`process_email_queue` is
|
||||||
:param list email_cc: optional list of string values for CC header (to be joined with commas)
|
called explicitly.
|
||||||
: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)
|
|
||||||
|
|
||||||
|
: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:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
if attachments is None:
|
if attachments is None:
|
||||||
attachments = {}
|
attachments = {}
|
||||||
|
if partner_ids is None:
|
||||||
|
partner_ids = []
|
||||||
attachment_obj = self.pool.get('ir.attachment')
|
attachment_obj = self.pool.get('ir.attachment')
|
||||||
for param in (email_to, email_cc, email_bcc):
|
for param in (email_to, email_cc, email_bcc):
|
||||||
if param and not isinstance(param, list):
|
if param and not isinstance(param, list):
|
||||||
param = [param]
|
param = [param]
|
||||||
msg_vals = {
|
msg_vals = {
|
||||||
'subject': subject,
|
'subject': subject,
|
||||||
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
|
'date': fields.datetime.now(),
|
||||||
'user_id': uid,
|
'user_id': uid,
|
||||||
'model': model,
|
'model': model,
|
||||||
'res_id': res_id,
|
'res_id': res_id,
|
||||||
'type': 'email',
|
'type': type,
|
||||||
'body_text': body if subtype != 'html' else False,
|
'body_text': body if content_subtype != 'html' else False,
|
||||||
'body_html': body if subtype == 'html' else False,
|
'body_html': body if content_subtype == 'html' else False,
|
||||||
'email_from': email_from,
|
'email_from': email_from,
|
||||||
'email_to': email_to and ','.join(email_to) or '',
|
'email_to': email_to and ','.join(email_to) or '',
|
||||||
'email_cc': email_cc and ','.join(email_cc) or '',
|
'email_cc': email_cc and ','.join(email_cc) or '',
|
||||||
'email_bcc': email_bcc and ','.join(email_bcc) or '',
|
'email_bcc': email_bcc and ','.join(email_bcc) or '',
|
||||||
|
'partner_ids': partner_ids,
|
||||||
'reply_to': reply_to,
|
'reply_to': reply_to,
|
||||||
'message_id': message_id,
|
'message_id': message_id,
|
||||||
'references': references,
|
'references': references,
|
||||||
'subtype': subtype,
|
'content_subtype': content_subtype,
|
||||||
'headers': headers, # serialize the dict on the fly
|
'headers': headers, # serialize the dict on the fly
|
||||||
'mail_server_id': mail_server_id,
|
'mail_server_id': mail_server_id,
|
||||||
'state': 'outgoing',
|
'state': 'outgoing',
|
||||||
|
@ -338,7 +387,10 @@ class mail_message(osv.osv):
|
||||||
return email_msg_id
|
return email_msg_id
|
||||||
|
|
||||||
def mark_outgoing(self, cr, uid, ids, context=None):
|
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):
|
def process_email_queue(self, cr, uid, ids=None, context=None):
|
||||||
"""Send immediately queued messages, committing after each
|
"""Send immediately queued messages, committing after each
|
||||||
|
@ -357,7 +409,7 @@ class mail_message(osv.osv):
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
if not ids:
|
if not ids:
|
||||||
filters = [('state', '=', 'outgoing')]
|
filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')]
|
||||||
if 'filters' in context:
|
if 'filters' in context:
|
||||||
filters.extend(context['filters'])
|
filters.extend(context['filters'])
|
||||||
ids = self.search(cr, uid, filters, context=context)
|
ids = self.search(cr, uid, filters, context=context)
|
||||||
|
@ -371,7 +423,7 @@ class mail_message(osv.osv):
|
||||||
_logger.exception("Failed processing mail queue")
|
_logger.exception("Failed processing mail queue")
|
||||||
return res
|
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
|
"""Parses a string or email.message.Message representing an
|
||||||
RFC-2822 email, and returns a generic dict holding the
|
RFC-2822 email, and returns a generic dict holding the
|
||||||
message details.
|
message details.
|
||||||
|
@ -394,7 +446,7 @@ class mail_message(osv.osv):
|
||||||
'headers' : { 'X-Mailer': mailer,
|
'headers' : { 'X-Mailer': mailer,
|
||||||
#.. all X- headers...
|
#.. all X- headers...
|
||||||
},
|
},
|
||||||
'subtype': msg_mime_subtype,
|
'content_subtype': msg_mime_subtype,
|
||||||
'body_text': plaintext_body
|
'body_text': plaintext_body
|
||||||
'body_html': html_body,
|
'body_html': html_body,
|
||||||
'attachments': [('file1', 'bytes'),
|
'attachments': [('file1', 'bytes'),
|
||||||
|
@ -428,49 +480,52 @@ class mail_message(osv.osv):
|
||||||
msg_txt['message-id'] = message_id
|
msg_txt['message-id'] = message_id
|
||||||
_logger.info('Parsing Message without message-id, generating a random one: %s', 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['id'] = message_id
|
||||||
msg['message-id'] = message_id
|
msg['message-id'] = message_id
|
||||||
|
|
||||||
if 'Subject' in fields:
|
if 'Subject' in msg_fields:
|
||||||
msg['subject'] = decode(msg_txt.get('Subject'))
|
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')
|
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())
|
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'))
|
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'))
|
msg['to'] = decode(msg_txt.get('Delivered-To'))
|
||||||
|
|
||||||
if 'CC' in fields:
|
if 'CC' in msg_fields:
|
||||||
msg['cc'] = decode(msg_txt.get('CC'))
|
msg['cc'] = decode(msg_txt.get('CC'))
|
||||||
|
|
||||||
if 'Cc' in fields:
|
if 'Cc' in msg_fields:
|
||||||
msg['cc'] = decode(msg_txt.get('Cc'))
|
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'))
|
msg['reply'] = decode(msg_txt.get('Reply-To'))
|
||||||
|
|
||||||
if 'Date' in fields:
|
if 'Date' in msg_fields:
|
||||||
date_hdr = decode(msg_txt.get('Date'))
|
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')
|
msg['encoding'] = msg_txt.get('Content-Transfer-Encoding')
|
||||||
|
|
||||||
if 'References' in fields:
|
if 'References' in msg_fields:
|
||||||
msg['references'] = msg_txt.get('References')
|
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['in-reply-to'] = msg_txt.get('In-Reply-To')
|
||||||
|
|
||||||
msg['headers'] = {}
|
msg['headers'] = {}
|
||||||
msg['subtype'] = 'plain'
|
msg['content_subtype'] = 'plain'
|
||||||
for item in msg_txt.items():
|
for item in msg_txt.items():
|
||||||
if item[0].startswith('X-'):
|
if item[0].startswith('X-'):
|
||||||
msg['headers'].update({item[0]: item[1]})
|
msg['headers'].update({item[0]: item[1]})
|
||||||
|
@ -479,7 +534,7 @@ class mail_message(osv.osv):
|
||||||
body = msg_txt.get_payload(decode=True)
|
body = msg_txt.get_payload(decode=True)
|
||||||
if 'text/html' in msg.get('content-type', ''):
|
if 'text/html' in msg.get('content-type', ''):
|
||||||
msg['body_html'] = body
|
msg['body_html'] = body
|
||||||
msg['subtype'] = 'html'
|
msg['content_subtype'] = 'html'
|
||||||
if body:
|
if body:
|
||||||
body = tools.html2plaintext(body)
|
body = tools.html2plaintext(body)
|
||||||
msg['body_text'] = tools.ustr(body, encoding)
|
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', ''):
|
if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', ''):
|
||||||
body = ""
|
body = ""
|
||||||
if 'multipart/alternative' in msg.get('content-type', ''):
|
if 'multipart/alternative' in msg.get('content-type', ''):
|
||||||
msg['subtype'] = 'alternative'
|
msg['content_subtype'] = 'alternative'
|
||||||
else:
|
else:
|
||||||
msg['subtype'] = 'mixed'
|
msg['content_subtype'] = 'mixed'
|
||||||
for part in msg_txt.walk():
|
for part in msg_txt.walk():
|
||||||
if part.get_content_maintype() == 'multipart':
|
if part.get_content_maintype() == 'multipart':
|
||||||
continue
|
continue
|
||||||
|
@ -504,7 +559,7 @@ class mail_message(osv.osv):
|
||||||
content = tools.ustr(content, encoding)
|
content = tools.ustr(content, encoding)
|
||||||
if part.get_content_subtype() == 'html':
|
if part.get_content_subtype() == 'html':
|
||||||
msg['body_html'] = content
|
msg['body_html'] = content
|
||||||
msg['subtype'] = 'html' # html version prevails
|
msg['content_subtype'] = 'html' # html version prevails
|
||||||
body = tools.ustr(tools.html2plaintext(content))
|
body = tools.ustr(tools.html2plaintext(content))
|
||||||
body = body.replace(' ', '')
|
body = body.replace(' ', '')
|
||||||
elif part.get_content_subtype() == 'plain':
|
elif part.get_content_subtype() == 'plain':
|
||||||
|
@ -521,7 +576,7 @@ class mail_message(osv.osv):
|
||||||
|
|
||||||
# for backwards compatibility:
|
# for backwards compatibility:
|
||||||
msg['body'] = msg['body_text']
|
msg['body'] = msg['body_text']
|
||||||
msg['sub_type'] = msg['subtype'] or 'plain'
|
msg['sub_type'] = msg['content_subtype'] or 'plain'
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def _postprocess_sent_message(self, cr, uid, message, context=None):
|
def _postprocess_sent_message(self, cr, uid, message, context=None):
|
||||||
|
@ -535,10 +590,9 @@ class mail_message(osv.osv):
|
||||||
"""
|
"""
|
||||||
if message.auto_delete:
|
if message.auto_delete:
|
||||||
self.pool.get('ir.attachment').unlink(cr, uid,
|
self.pool.get('ir.attachment').unlink(cr, uid,
|
||||||
[x.id for x in message.attachment_ids \
|
[x.id for x in message.attachment_ids
|
||||||
if x.res_model == self._name and \
|
if x.res_model == self._name and x.res_id == message.id],
|
||||||
x.res_id == message.id],
|
context=context)
|
||||||
context=context)
|
|
||||||
message.unlink()
|
message.unlink()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -557,8 +611,6 @@ class mail_message(osv.osv):
|
||||||
transactions (default: False)
|
transactions (default: False)
|
||||||
:return: True
|
:return: True
|
||||||
"""
|
"""
|
||||||
if context is None:
|
|
||||||
context = {}
|
|
||||||
ir_mail_server = self.pool.get('ir.mail_server')
|
ir_mail_server = self.pool.get('ir.mail_server')
|
||||||
self.write(cr, uid, ids, {'state': 'outgoing'}, context=context)
|
self.write(cr, uid, ids, {'state': 'outgoing'}, context=context)
|
||||||
for message in self.browse(cr, uid, ids, 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:
|
for attach in message.attachment_ids:
|
||||||
attachments.append((attach.datas_fname, base64.b64decode(attach.datas)))
|
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
|
body_alternative = None
|
||||||
subtype_alternative = None
|
content_subtype_alternative = None
|
||||||
if message.subtype == 'html' and message.body_text:
|
if message.content_subtype == 'html' and message.body_text:
|
||||||
# we have a plain text alternative prepared, pass it to
|
# we have a plain text alternative prepared, pass it to
|
||||||
# build_message instead of letting it build one
|
# build_message instead of letting it build one
|
||||||
body_alternative = message.body_text
|
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(
|
msg = ir_mail_server.build_email(
|
||||||
email_from=message.email_from,
|
email_from=message.email_from,
|
||||||
email_to=to_email(message.email_to),
|
email_to=mail_tools_to_email(message_email_to),
|
||||||
subject=message.subject,
|
subject=message.subject,
|
||||||
body=body,
|
body=body,
|
||||||
body_alternative=body_alternative,
|
body_alternative=body_alternative,
|
||||||
email_cc=to_email(message.email_cc),
|
email_cc=mail_tools_to_email(message.email_cc),
|
||||||
email_bcc=to_email(message.email_bcc),
|
email_bcc=mail_tools_to_email(message.email_bcc),
|
||||||
reply_to=message.reply_to,
|
reply_to=message.reply_to,
|
||||||
attachments=attachments, message_id=message.message_id,
|
attachments=attachments, message_id=message.message_id,
|
||||||
references = message.references,
|
references = message.references,
|
||||||
object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)),
|
object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)),
|
||||||
subtype=message.subtype,
|
subtype=message.content_subtype,
|
||||||
subtype_alternative=subtype_alternative,
|
subtype_alternative=content_subtype_alternative,
|
||||||
headers=message.headers and ast.literal_eval(message.headers))
|
headers=message.headers and ast.literal_eval(message.headers))
|
||||||
res = ir_mail_server.send_email(cr, uid, msg,
|
res = ir_mail_server.send_email(cr, uid, msg,
|
||||||
mail_server_id=message.mail_server_id.id,
|
mail_server_id=message.mail_server_id.id,
|
||||||
context=context)
|
context=context)
|
||||||
if res:
|
if res:
|
||||||
message.write({'state':'sent', 'message_id': res})
|
message.write({'state':'sent', 'message_id': res, 'email_to': message_email_to})
|
||||||
else:
|
else:
|
||||||
message.write({'state':'exception'})
|
message.write({'state':'exception', 'email_to': message_email_to})
|
||||||
message.refresh()
|
message.refresh()
|
||||||
if message.state == 'sent':
|
if message.state == 'sent':
|
||||||
self._postprocess_sent_message(cr, uid, message, context=context)
|
self._postprocess_sent_message(cr, uid, message, context=context)
|
||||||
|
@ -609,8 +669,6 @@ class mail_message(osv.osv):
|
||||||
cr.commit()
|
cr.commit()
|
||||||
return True
|
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:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -30,17 +30,26 @@
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="subject"/>
|
<field name="subject"/>
|
||||||
|
<field name="user_id"/>
|
||||||
<field name="date"/>
|
<field name="date"/>
|
||||||
<field name="type"/>
|
<field name="type"/>
|
||||||
<field name="body_text"/>
|
<field name="content_subtype"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="user_id" string="User"/>
|
|
||||||
<field name="model"/>
|
<field name="model"/>
|
||||||
<field name="res_id"/>
|
<field name="res_id"/>
|
||||||
<field name="parent_id"/>
|
<field name="parent_id"/>
|
||||||
|
<field name="partner_ids" widget="many2many_tags"/>
|
||||||
</group>
|
</group>
|
||||||
</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>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
|
@ -73,7 +82,30 @@
|
||||||
<search string="Messages Search">
|
<search string="Messages Search">
|
||||||
<field name="user_id"/>
|
<field name="user_id"/>
|
||||||
<field name="body"/>
|
<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>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -86,55 +118,60 @@
|
||||||
<form string="Email message" version="7.0">
|
<form string="Email message" version="7.0">
|
||||||
<sheet>
|
<sheet>
|
||||||
<label for="subject" class="oe_edit_only"/>
|
<label for="subject" class="oe_edit_only"/>
|
||||||
<h1><field name="subject"/></h1>
|
<h2><field name="subject"/></h2>
|
||||||
<label for="user_id"/>
|
<div>
|
||||||
<h2><field name="user_id" class="oe_inline" string="User"/> on <field name="date" class="oe_inline"/></h2>
|
by <field name="user_id" class="oe_inline" string="User"/> on <field name="date" class="oe_inline"/>
|
||||||
<group col="4">
|
<button name="%(action_email_compose_message_wizard)d" string="Reply" type="action" icon="terp-mail-replied"
|
||||||
<field name="partner_id" readonly="1" attrs="{'invisible':[('partner_id', '=', False)]}"/>
|
context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" states='received,sent,exception,cancel'/>
|
||||||
<field name="type"/>
|
</div>
|
||||||
</group>
|
|
||||||
<notebook colspan="4">
|
<notebook colspan="4">
|
||||||
<page string="Details">
|
<page string="Message Details">
|
||||||
<group string="Recipients">
|
<group>
|
||||||
<field name="email_from"/>
|
<group>
|
||||||
<field name="email_to"/>
|
<field name="email_from"/>
|
||||||
<field name="email_cc" attrs="{'invisible':[('email_cc', '=', False)]}"/>
|
<field name="email_to"/>
|
||||||
<field name="email_bcc" attrs="{'invisible':[('email_bcc', '=', False)]}"/>
|
<field name="email_cc"/>
|
||||||
<field name="reply_to" attrs="{'invisible':[('reply_to', '=', False)]}"/>
|
<field name="email_bcc"/>
|
||||||
</group>
|
<field name="reply_to"/>
|
||||||
<group col="4" string="Message Details">
|
</group>
|
||||||
<field name="model"/>
|
<group>
|
||||||
<button name="open_document" string="Open" type="object" icon="gtk-jump-to" colspan="2"/>
|
<field name="partner_id" readonly="1"/>
|
||||||
<field name="res_id"/>
|
<field name="partner_ids" widget="many2many_tags"/>
|
||||||
<field name="message_id" colspan="4" attrs="{'invisible':[('message_id', '=', False)]}"/>
|
</group>
|
||||||
<field name="references" colspan="4" widget="char" size="512" attrs="{'invisible':[('references', '=', False)]}"/>
|
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<notebook>
|
||||||
<page string="Body (Rich)" attrs="{'invisible':[('subtype','=','plain')]}">
|
<page string="Body (Rich)">
|
||||||
<field name="body_html" widget="text_html"/>
|
<field name="body_html" widget="text_html"/>
|
||||||
</page>
|
</page>
|
||||||
<page string="Body (Plain)">
|
<page string="Body (Plain)">
|
||||||
<field name="body_text" widget="text"/>
|
<field name="body_text" widget="text"/>
|
||||||
</page>
|
</page>
|
||||||
</notebook>
|
</notebook>
|
||||||
<group col="5">
|
</page>
|
||||||
<field name="state" colspan="2"/>
|
<page string="Advanced" groups="base.group_no_one">
|
||||||
<field name="subtype" attrs="{'invisible':[('subtype', '=', False)]}"/>
|
<group>
|
||||||
<button name="%(action_email_compose_message_wizard)d" string="Reply" type="action" icon="terp-mail-replied"
|
<group>
|
||||||
context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" states='received,sent,exception,cancel'/>
|
<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>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
<page string="Attachments">
|
<page string="Attachments">
|
||||||
<field name="attachment_ids"/>
|
<field name="attachment_ids"/>
|
||||||
</page>
|
</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>
|
</notebook>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
|
@ -236,13 +273,13 @@
|
||||||
|
|
||||||
<record id="action_mail_all_feeds" model="ir.actions.client">
|
<record id="action_mail_all_feeds" model="ir.actions.client">
|
||||||
<field name="name">News Feed</field>
|
<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')}"/>
|
<field name="params" eval="{'search_view_id': ref('view_message_search_wall')}"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_mail_my_feeds" model="ir.actions.client">
|
<record id="action_mail_my_feeds" model="ir.actions.client">
|
||||||
<field name="name">My Feeds</field>
|
<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}"/>
|
<field name="params" eval="{'search_view_id': ref('view_message_search_wall'), 'my_feeds': True}"/>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -52,11 +52,13 @@
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- Add menu entry in Settings/Email -->
|
<!-- 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"/>
|
<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 -->
|
<!-- Add notifications related 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"/>
|
<menuitem name="Notifications" id="menu_email_notifications" parent="base.menu_email"
|
||||||
|
action="action_view_notifications" sequence="35" groups="base.group_no_one"/>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -65,7 +65,7 @@ class mail_thread(osv.Model):
|
||||||
def _get_message_ids(self, cr, uid, ids, name, args, context=None):
|
def _get_message_ids(self, cr, uid, ids, name, args, context=None):
|
||||||
res = {}
|
res = {}
|
||||||
for id in ids:
|
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)
|
subscriber_ids = self.message_get_subscribers(cr, uid, [id], context=context)
|
||||||
res[id] = {
|
res[id] = {
|
||||||
'message_ids': message_ids,
|
'message_ids': message_ids,
|
||||||
|
@ -130,7 +130,7 @@ class mail_thread(osv.Model):
|
||||||
# delete subscriptions
|
# delete subscriptions
|
||||||
subscr_to_del_ids = subscr_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
|
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)
|
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_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)
|
msg_obj.unlink(cr, uid, msg_to_del_ids, context=context)
|
||||||
|
|
||||||
|
@ -150,39 +150,13 @@ class mail_thread(osv.Model):
|
||||||
context = {}
|
context = {}
|
||||||
|
|
||||||
message_obj = self.pool.get('mail.message')
|
message_obj = self.pool.get('mail.message')
|
||||||
subscription_obj = self.pool.get('mail.subscription')
|
|
||||||
notification_obj = self.pool.get('mail.notification')
|
notification_obj = self.pool.get('mail.notification')
|
||||||
res_users_obj = self.pool.get('res.users')
|
body = vals.get('body_html', '') if vals.get('content_subtype') == 'html' else vals.get('body_text', '')
|
||||||
body = vals.get('body_html', '') if vals.get('subtype', 'plain') == 'html' else vals.get('body_text', '')
|
|
||||||
|
|
||||||
# automatically subscribe the writer of the message
|
# automatically subscribe the writer of the message
|
||||||
if vals['user_id']:
|
if vals['user_id']:
|
||||||
self.message_subscribe(cr, uid, [thread_id], [vals['user_id']], context=context)
|
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
|
# create message
|
||||||
msg_id = message_obj.create(cr, uid, vals, context=context)
|
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
|
# special: if install mode, do not push demo data
|
||||||
if context.get('install_mode', False):
|
if context.get('install_mode', False):
|
||||||
return True
|
return msg_id
|
||||||
|
|
||||||
# push to users
|
# 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:
|
for id in user_to_push_ids:
|
||||||
notification_obj.create(cr, uid, {'user_id': id, 'message_id': msg_id}, context=context)
|
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
|
return msg_id
|
||||||
|
|
||||||
def message_create_get_notification_user_ids(self, cr, uid, thread_ids, new_msg_vals, context=None):
|
def message_get_user_ids_to_notify(self, cr, uid, thread_ids, new_msg_vals, context=None):
|
||||||
if context is None:
|
subscription_obj = self.pool.get('mail.subscription')
|
||||||
context = {}
|
# get body
|
||||||
|
body = new_msg_vals.get('body_html', '') if new_msg_vals.get('content_subtype') == 'html' else new_msg_vals.get('body_text', '')
|
||||||
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', '')
|
# get subscribers
|
||||||
for thread_id in thread_ids:
|
notif_user_ids = self.message_get_subscribers(cr, uid, thread_ids, context=context)
|
||||||
# add subscribers
|
|
||||||
notif_user_ids += self.message_get_subscribers(cr, uid, [thread_id], context=context)
|
# add users requested via parsing message (@login)
|
||||||
# add users requested via parsing message (@login)
|
notif_user_ids += self.message_parse_users(cr, uid, body, context=context)
|
||||||
notif_user_ids += self.message_parse_users(cr, uid, [thread_id], body, context=context)
|
|
||||||
# add users requested to perform an action (need_action mechanism)
|
# add users requested to perform an action (need_action mechanism)
|
||||||
if hasattr(self, 'get_needaction_user_ids'):
|
if hasattr(self, 'get_needaction_user_ids'):
|
||||||
notif_user_ids += self.get_needaction_user_ids(cr, uid, [thread_id], context=context)[thread_id]
|
user_ids_dict = self.get_needaction_user_ids(cr, uid, thread_ids, context=context)
|
||||||
# add users notified of the parent messages (because: if parent message contains @login, login must receive the replies)
|
for id, user_ids in user_ids_dict.iteritems():
|
||||||
if new_msg_vals.get('parent_id'):
|
notif_user_ids += user_ids
|
||||||
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)
|
# add users notified of the parent messages (because: if parent message contains @login, login must receive the replies)
|
||||||
parent_notifs = notif_obj.read(cr, uid, parent_notif_ids, context=context)
|
if new_msg_vals.get('parent_id'):
|
||||||
notif_user_ids += [parent_notif['user_id'][0] for parent_notif in parent_notifs]
|
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
|
# remove duplicate entries
|
||||||
notif_user_ids = list(set(notif_user_ids))
|
notif_user_ids = list(set(notif_user_ids))
|
||||||
return 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
|
"""Parse message content
|
||||||
- if find @login -(^|\s)@((\w|@|\.)*)-: returns the related ids
|
- 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|@|\.)*)')
|
regex = re.compile('(^|\s)@((\w|@|\.)*)')
|
||||||
login_lst = [item[1] for item in regex.findall(string)]
|
login_lst = [item[1] for item in regex.findall(string)]
|
||||||
|
@ -248,46 +229,61 @@ class mail_thread(osv.Model):
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None,
|
def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None,
|
||||||
parent_id=False, type='email', subtype='plain', state='received',
|
type='email', email_date=None, parent_id=False,
|
||||||
email_to=False, email_from=False, email_cc=None, email_bcc=None,
|
content_subtype='plain', state=None,
|
||||||
reply_to=None, email_date=None, message_id=False, references=None,
|
partner_ids=None, email_from=False, email_to=False,
|
||||||
attachments=None, headers=None, original=None, context=None):
|
email_cc=None, email_bcc=None, reply_to=None,
|
||||||
"""Creates a new mail.message attached to the current mail.thread,
|
headers=None, message_id=False, references=None,
|
||||||
containing all the details passed as parameters. All attachments
|
attachments=None, original=None, context=None):
|
||||||
will be attached to the thread record as well as to the actual
|
""" Creates a new mail.message through message_create. The new message
|
||||||
message.
|
is attached to the current mail.thread, containing all the details
|
||||||
If ``email_from`` is not set or ``type`` not set as 'email',
|
passed as parameters. All attachments will be attached to the
|
||||||
a note message is created, without the usual envelope
|
thread record as well as to the actual message.
|
||||||
attributes (sender, recipients, etc.).
|
|
||||||
The creation of the message is done by calling ``message_create``
|
This method calls message_create that will handle management of
|
||||||
method, that will manage automatic pushing of notifications.
|
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
|
:param threads: list of thread ids, or list of browse_records
|
||||||
threads to which a new message should be attached
|
representing threads to which a new message should be attached
|
||||||
:param subject: subject of the message, or description of the event if this
|
:param subject: subject of the message, or description of the event;
|
||||||
is an *event log* entry.
|
this is totally optional as subjects are not important except
|
||||||
:param body_text: plaintext contents of the mail or log message
|
for specific messages (blog post, job offers) or for emails
|
||||||
:param body_html: html contents of the mail or log message
|
:param body_text: plaintext contents of the mail or log message
|
||||||
:param parent_id: id of the parent message (threaded messaging model)
|
:param body_html: html contents of the mail or log message
|
||||||
:param type: optional type of message: 'email', 'comment', 'notification'
|
:param type: type of message: 'email', 'comment', 'notification';
|
||||||
:param subtype: optional subtype of message: 'plain' or 'html', corresponding to the main
|
email by default
|
||||||
body contents (body_text or body_html).
|
:param email_date: email date string if different from now, in
|
||||||
:param state: optional state of message; 'received' by default
|
server timezone
|
||||||
:param email_to: Email-To / Recipient address
|
:param parent_id: id of the parent message (threaded messaging model)
|
||||||
:param email_from: Email From / Sender address if any
|
:param content_subtype: optional content_subtype of message: 'plain'
|
||||||
:param email_cc: Comma-Separated list of Carbon Copy Emails To addresse if any
|
or 'html', corresponding to the main body contents (body_text or
|
||||||
:param email_bcc: Comma-Separated list of Blind Carbon Copy Emails To addresses if any
|
body_html).
|
||||||
:param reply_to: reply_to header
|
:param state: state of message
|
||||||
:param email_date: email date string if different from now, in server timezone
|
:param partner_ids: destination partners of the message, in addition
|
||||||
:param message_id: optional email identifier
|
to the now fully optional email_to; this method is supposed to
|
||||||
:param references: optional email references
|
received a list of ids is not None. The specific many2many
|
||||||
:param headers: mail headers to store
|
instruction will be generated by this method.
|
||||||
:param dict attachments: map of attachment filenames to binary contents, if any.
|
:param email_from: Email From / Sender address if any
|
||||||
:param str original: optional full source of the RFC2822 email, for reference
|
:param email_to: Email-To / Recipient address
|
||||||
:param dict context: if a ``thread_model`` value is present
|
:param email_cc: Comma-Separated list of Carbon Copy Emails To
|
||||||
in the context, its value will be used
|
addresses if any
|
||||||
to determine the model of the thread to
|
:param email_bcc: Comma-Separated list of Blind Carbon Copy Emails To
|
||||||
update (instead of the current model).
|
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:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
|
@ -323,10 +319,15 @@ class mail_thread(osv.Model):
|
||||||
'res_id': thread.id,
|
'res_id': thread.id,
|
||||||
}
|
}
|
||||||
to_attach.append(ir_attachment.create(cr, uid, data_attach, context=context))
|
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
|
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':
|
if not partner_id and thread._name == 'res.partner':
|
||||||
partner_id = thread.id
|
partner_id = thread.id
|
||||||
|
# destination partners
|
||||||
|
if partner_ids is None:
|
||||||
|
partner_ids = []
|
||||||
|
mail_partner_ids = [(6, 0, partner_ids)]
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'subject': subject,
|
'subject': subject,
|
||||||
'body_text': body_text or (hasattr(thread, 'description') and thread.description or ''),
|
'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,
|
'parent_id': parent_id,
|
||||||
'date': email_date or fields.datetime.now(),
|
'date': email_date or fields.datetime.now(),
|
||||||
'type': type,
|
'type': type,
|
||||||
'subtype': subtype,
|
'content_subtype': content_subtype,
|
||||||
'state': state,
|
'state': state,
|
||||||
'message_id': message_id,
|
'message_id': message_id,
|
||||||
|
'partner_ids': mail_partner_ids,
|
||||||
'attachment_ids': [(6, 0, to_attach)],
|
'attachment_ids': [(6, 0, to_attach)],
|
||||||
'user_id': uid,
|
'user_id': uid,
|
||||||
'model' : thread._name,
|
'model' : thread._name,
|
||||||
|
@ -349,7 +351,6 @@ class mail_thread(osv.Model):
|
||||||
if isinstance(param, list):
|
if isinstance(param, list):
|
||||||
param = ", ".join(param)
|
param = ", ".join(param)
|
||||||
data.update({
|
data.update({
|
||||||
'subject': subject or _('History'),
|
|
||||||
'email_to': email_to,
|
'email_to': email_to,
|
||||||
'email_from': email_from or \
|
'email_from': email_from or \
|
||||||
(hasattr(thread, 'user_id') and thread.user_id and thread.user_id.user_email),
|
(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'),
|
body_html= msg_dict.get('body_html'),
|
||||||
parent_id = msg_dict.get('parent_id', False),
|
parent_id = msg_dict.get('parent_id', False),
|
||||||
type = msg_dict.get('type', 'email'),
|
type = msg_dict.get('type', 'email'),
|
||||||
subtype = msg_dict.get('subtype', 'plain'),
|
content_subtype = msg_dict.get('content_subtype'),
|
||||||
state = msg_dict.get('state', 'received'),
|
state = msg_dict.get('state'),
|
||||||
|
partner_ids = msg_dict.get('partner_ids'),
|
||||||
email_from = msg_dict.get('from', msg_dict.get('email_from')),
|
email_from = msg_dict.get('from', msg_dict.get('email_from')),
|
||||||
email_to = msg_dict.get('to', msg_dict.get('email_to')),
|
email_to = msg_dict.get('to', msg_dict.get('email_to')),
|
||||||
email_cc = msg_dict.get('cc', msg_dict.get('email_cc')),
|
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'),
|
original = msg_dict.get('original'),
|
||||||
context = context)
|
context = context)
|
||||||
|
|
||||||
def _message_add_ancestor_ids(self, cr, uid, ids, child_ids, root_ids, context=None):
|
#------------------------------------------------------
|
||||||
""" Given message child_ids
|
# Message loading
|
||||||
Find their ancestors until root ids"""
|
#------------------------------------------------------
|
||||||
if context is None:
|
|
||||||
context = {}
|
def _message_search_ancestor_ids(self, cr, uid, ids, child_ids, ancestor_ids, context=None):
|
||||||
msg_obj = self.pool.get('mail.message')
|
""" Given message child_ids ids, find their ancestors until ancestor_ids
|
||||||
tmp_msgs = msg_obj.read(cr, uid, child_ids, ['id', 'parent_id'], context=context)
|
using their parent_id relationship.
|
||||||
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]
|
|
||||||
|
: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
|
child_ids += parent_ids
|
||||||
cur_iter = 0; max_iter = 100; # avoid infinite loop
|
cur_iter = 0; max_iter = 100; # avoid infinite loop
|
||||||
while (parent_ids and (cur_iter < max_iter)):
|
while (parent_ids and (cur_iter < max_iter)):
|
||||||
cur_iter += 1
|
cur_iter += 1
|
||||||
tmp_msgs = msg_obj.read(cr, uid, parent_ids, ['id', 'parent_id'], context=context)
|
messages_temp = message_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]
|
parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids)
|
||||||
child_ids += parent_ids
|
child_ids += parent_ids
|
||||||
if (cur_iter > max_iter):
|
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
|
return child_ids
|
||||||
|
|
||||||
def message_load_ids(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[], context=None):
|
def message_search_get_domain(self, cr, uid, ids, context=None):
|
||||||
""" OpenChatter feature: return thread messages ids. It searches in
|
""" OpenChatter feature: get the domain to search the messages related
|
||||||
mail.messages where res_id = ids, (res_)model = current model.
|
to a document. mail.thread defines the default behavior as
|
||||||
:param domain: domain to add to the search; especially child_of
|
being messages with model = self._name, id in ids.
|
||||||
is interesting when dealing with threaded display
|
This method should be overridden if a model has to implement a
|
||||||
:param ascent: performs an ascended search; will add to fetched msgs
|
particular behavior.
|
||||||
all their parents until root_ids
|
|
||||||
:param root_ids: for ascent search
|
|
||||||
:param root_ids: root_ids when performing an ascended search
|
|
||||||
"""
|
"""
|
||||||
if context is None:
|
return ['&', ('res_id', 'in', ids), ('model', '=', self._name)]
|
||||||
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
|
|
||||||
|
|
||||||
def message_load(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[], context=None):
|
def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None,
|
||||||
""" OpenChatter feature: return thread messages
|
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)
|
search_domain = self.message_search_get_domain(cr, uid, ids, context=context)
|
||||||
msgs = self.pool.get('mail.message').read(cr, uid, msg_ids, [], context=context)
|
if domain:
|
||||||
|
search_domain += domain
|
||||||
# Set as read
|
message_obj = self.pool.get('mail.message')
|
||||||
self.message_check_and_set_read(cr, uid, ids, context=context)
|
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 """
|
""" 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 = {}
|
map_id_to_name = {}
|
||||||
|
for msg in messages:
|
||||||
for msg in msgs:
|
|
||||||
for attach_id in msg["attachment_ids"]:
|
for attach_id in msg["attachment_ids"]:
|
||||||
map_id_to_name[attach_id] = '' # use empty string as a placeholder
|
map_id_to_name[attach_id] = '' # use empty string as a placeholder
|
||||||
|
|
||||||
ids = map_id_to_name.keys()
|
ids = map_id_to_name.keys()
|
||||||
names = self.pool.get('ir.attachment').name_get(cr, uid, ids, context=context)
|
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]
|
map_id_to_name[name[0]] = name[1]
|
||||||
|
|
||||||
# give corresponding ids and names to each message
|
# give corresponding ids and names to each message
|
||||||
for msg in msgs:
|
for msg in messages:
|
||||||
msg["attachments"] = []
|
msg["attachments"] = []
|
||||||
|
|
||||||
for attach_id in msg["attachment_ids"]:
|
for attach_id in msg["attachment_ids"]:
|
||||||
msg["attachments"].append({'id': attach_id, 'name': map_id_to_name[attach_id]})
|
msg["attachments"].append({'id': attach_id, 'name': map_id_to_name[attach_id]})
|
||||||
|
|
||||||
""" Sort and return messages """
|
# Set the threads as read
|
||||||
msgs = sorted(msgs, key=lambda d: (-d['id']))
|
self.message_check_and_set_read(cr, uid, ids, context=context)
|
||||||
return msgs
|
# Sort and return the messages
|
||||||
|
messages = sorted(messages, key=lambda d: (-d['id']))
|
||||||
|
return messages
|
||||||
|
|
||||||
def get_pushed_messages(self, cr, uid, ids, limit=100, offset=0, msg_search_domain=[], ascent=False, root_ids=[], context=None):
|
def message_get_pushed_messages(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None,
|
||||||
""" OpenChatter: wall: get messages to display (=pushed notifications)
|
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
|
:param domain: domain to add to the search; especially child_of
|
||||||
is interesting when dealing with threaded display
|
is interesting when dealing with threaded display
|
||||||
:param ascent: performs an ascended search; will add to fetched msgs
|
:param ascent: performs an ascended search; will add to fetched msgs
|
||||||
all their parents until root_ids
|
all their parents until root_ids
|
||||||
:param root_ids: for ascent search
|
: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')
|
notification_obj = self.pool.get('mail.notification')
|
||||||
msg_obj = self.pool.get('mail.message')
|
msg_obj = self.pool.get('mail.message')
|
||||||
# update message search
|
# update message search
|
||||||
|
@ -496,7 +556,7 @@ class mail_thread(osv.Model):
|
||||||
msg_ids = [notification.message_id.id for notification in notifications]
|
msg_ids = [notification.message_id.id for notification in notifications]
|
||||||
# get messages
|
# get messages
|
||||||
msg_ids = msg_obj.search(cr, uid, [('id', 'in', msg_ids)], context=context)
|
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)
|
msgs = msg_obj.read(cr, uid, msg_ids, context=context)
|
||||||
return msgs
|
return msgs
|
||||||
|
|
||||||
|
@ -524,9 +584,9 @@ class mail_thread(osv.Model):
|
||||||
record needs to be created. Ignored
|
record needs to be created. Ignored
|
||||||
if the thread record already exists.
|
if the thread record already exists.
|
||||||
:param bool save_original: whether to keep a copy of the original
|
: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
|
: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
|
# 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
|
# 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})
|
context.update({'thread_model': model})
|
||||||
|
|
||||||
mail_message = self.pool.get('mail.message')
|
mail_message = self.pool.get('mail.message')
|
||||||
res_id = False
|
|
||||||
|
|
||||||
# Parse Message
|
# Parse Message
|
||||||
# Warning: message_from_string doesn't always work correctly on unicode,
|
# Warning: message_from_string doesn't always work correctly on unicode,
|
||||||
|
@ -548,8 +607,11 @@ class mail_thread(osv.Model):
|
||||||
if isinstance(message, unicode):
|
if isinstance(message, unicode):
|
||||||
message = message.encode('utf-8')
|
message = message.encode('utf-8')
|
||||||
msg_txt = email.message_from_string(message)
|
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:
|
if strip_attachments and 'attachments' in msg:
|
||||||
del msg['attachments']
|
del msg['attachments']
|
||||||
|
|
||||||
|
@ -587,6 +649,8 @@ class mail_thread(osv.Model):
|
||||||
res_id = create_record(msg)
|
res_id = create_record(msg)
|
||||||
# To forward the email to other followers
|
# To forward the email to other followers
|
||||||
self.message_forward(cr, uid, model, [res_id], msg_txt, context=context)
|
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
|
return res_id
|
||||||
|
|
||||||
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
|
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)
|
return self.message_append_dict(cr, uid, ids, msg_dict, context=context)
|
||||||
|
|
||||||
def message_thread_followers(self, cr, uid, ids, context=None):
|
def message_thread_followers(self, cr, uid, ids, context=None):
|
||||||
"""Returns a list of email addresses of the people following
|
""" Returns a list of email addresses of the people following
|
||||||
this thread, including the sender of each mail, and the
|
this thread, including the sender of each mail, and the
|
||||||
people who were in CC of the messages, if any.
|
people who were in CC of the messages, if any.
|
||||||
"""
|
"""
|
||||||
res = {}
|
res = {}
|
||||||
if isinstance(ids, (str, int, long)):
|
if isinstance(ids, (str, int, long)):
|
||||||
|
@ -748,45 +812,27 @@ class mail_thread(osv.Model):
|
||||||
# Note specific
|
# 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):
|
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)
|
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):
|
def message_append_note(self, cr, uid, ids, subject=None, body=None, parent_id=False,
|
||||||
if type in ['notification', 'reply']:
|
type='notification', content_subtype='html', context=None):
|
||||||
|
if type in ['notification', 'comment']:
|
||||||
subject = None
|
subject = None
|
||||||
if subtype == 'html':
|
if content_subtype == 'html':
|
||||||
body_html = body
|
body_html = body
|
||||||
body_text = body
|
body_text = body
|
||||||
else:
|
else:
|
||||||
body_html = body
|
body_html = body
|
||||||
body_text = 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
|
# Subscription mechanism
|
||||||
|
@ -796,9 +842,9 @@ class mail_thread(osv.Model):
|
||||||
""" Returns the current document followers. Basically this method
|
""" Returns the current document followers. Basically this method
|
||||||
checks in mail.subscription for entries with matching res_model,
|
checks in mail.subscription for entries with matching res_model,
|
||||||
res_id.
|
res_id.
|
||||||
|
This method can be overriden to add implicit subscribers, such
|
||||||
:param get_ids: if set to True, return the ids of users; if set
|
as project managers, by adding their user_id to the list of
|
||||||
to False, returns the result of a read in res.users
|
ids returned by this method.
|
||||||
"""
|
"""
|
||||||
subscr_obj = self.pool.get('mail.subscription')
|
subscr_obj = self.pool.get('mail.subscription')
|
||||||
subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
|
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
|
# Trying to unsubscribe somebody not in subscribers: returns False
|
||||||
# if special management is needed; allows to know that an automatically
|
# if special management is needed; allows to know that an automatically
|
||||||
# subscribed user tries to unsubscribe and allows to warn him
|
# 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
|
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,
|
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)
|
['&', '&', ('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)
|
return subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context)
|
||||||
|
|
||||||
#------------------------------------------------------
|
#------------------------------------------------------
|
||||||
# Notification API
|
# 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):
|
def message_remove_pushed_notifications(self, cr, uid, ids, msg_ids, remove_childs=True, context=None):
|
||||||
notif_obj = self.pool.get('mail.notification')
|
notif_obj = self.pool.get('mail.notification')
|
||||||
msg_obj = self.pool.get('mail.message')
|
msg_obj = self.pool.get('mail.message')
|
||||||
|
|
|
@ -22,34 +22,18 @@
|
||||||
from osv import osv
|
from osv import osv
|
||||||
from osv import fields
|
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 """
|
""" Inherits partner and adds CRM information in the partner form """
|
||||||
_name = "res.partner"
|
_name = "res.partner"
|
||||||
_inherit = ['res.partner', 'mail.thread']
|
_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):
|
def message_search_get_domain(self, cr, uid, ids, context=None):
|
||||||
""" Override of message_load_ids
|
""" Override of message_search_get_domain for partner discussion page.
|
||||||
partner discussion page :
|
The purpose is to add messages directly sent to the partner.
|
||||||
- messages posted on res.partner, partner_id = partner.id
|
|
||||||
- messages directly sent to partner
|
|
||||||
"""
|
"""
|
||||||
msg_obj = self.pool.get('mail.message')
|
initial_domain = super(res_partner_mail, self).message_search_get_domain(cr, uid, ids, context=context)
|
||||||
msg_ids = []
|
if self._name == 'res.partner': # to avoid models inheriting from res.partner
|
||||||
partner_ids=[]
|
search_domain = ['|'] + initial_domain + ['|', ('partner_id', 'in', ids), ('partner_ids', 'in', ids)]
|
||||||
for partner in self.browse(cr, uid, ids, context=context):
|
return search_domain
|
||||||
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()
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//sheet" position="after">
|
<xpath expr="//sheet" position="after">
|
||||||
<div class="oe_chatter">
|
<div class="oe_chatter oe_mail_group_footer">
|
||||||
<field name="message_ids" widget="mail_thread"
|
<field name="message_ids" widget="mail_thread"
|
||||||
options='{"thread_level": 1}'/>
|
options='{"thread_level": 1}'/>
|
||||||
</div>
|
</div>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
|
@ -24,24 +24,26 @@ from tools.translate import _
|
||||||
|
|
||||||
class res_users(osv.osv):
|
class res_users(osv.osv):
|
||||||
""" Update of res.users class
|
""" 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
|
- make a new user follow itself
|
||||||
|
- add a welcome message
|
||||||
"""
|
"""
|
||||||
_name = 'res.users'
|
_name = 'res.users'
|
||||||
_inherit = ['res.users', 'mail.thread']
|
_inherit = ['res.users', 'mail.thread']
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'notification_email_pref': fields.selection([
|
'notification_email_pref': fields.selection([
|
||||||
('all', 'All feeds'),
|
('all', 'All feeds'),
|
||||||
('comments', 'Only comments'),
|
('comments', 'Only comments'),
|
||||||
('to_me', 'Only when sent directly to me'),
|
('to_me', 'Only when sent directly to me'),
|
||||||
('none', 'Never')
|
('none', 'Never')
|
||||||
], 'Receive Feeds by Email', required=True,
|
], 'Receive Feeds by Email', required=True,
|
||||||
help="Choose in which case you want to receive an email when you receive new feeds."),
|
help="Choose in which case you want to receive an email when you "\
|
||||||
|
"receive new feeds."),
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'notification_email_pref': 'none',
|
'notification_email_pref': 'to_me',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, pool, cr):
|
def __init__(self, pool, cr):
|
||||||
|
@ -60,25 +62,61 @@ class res_users(osv.osv):
|
||||||
user = self.browse(cr, uid, [user_id], context=context)[0]
|
user = self.browse(cr, uid, [user_id], context=context)[0]
|
||||||
# make user follow itself
|
# make user follow itself
|
||||||
self.message_subscribe(cr, uid, [user_id], [user_id], context=context)
|
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'
|
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)
|
message = _('%s has joined %s! Welcome in 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_append_note(cr, uid, [user_id], subject='Welcom to OpenERP', body=message, type='comment', context=context)
|
||||||
#self.message_broadcast(cr, uid, [user.id], 'Welcome notification', message, context=context)
|
|
||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
def message_load_ids(self, cr, uid, ids, limit=100, offset=0, domain=[], ascent=False, root_ids=[False], context=None):
|
def message_search_get_domain(self, cr, uid, ids, context=None):
|
||||||
""" Override of message_load_ids
|
""" Override of message_search_get_domain for partner discussion page.
|
||||||
User discussion page :
|
The purpose is to add messages directly sent to user using
|
||||||
- messages posted on res.users, res_id = user.id
|
@user_login.
|
||||||
- messages directly sent to user with @user_login
|
|
||||||
"""
|
"""
|
||||||
if context is None:
|
initial_domain = super(res_users, self).message_search_get_domain(cr, uid, ids, context=context)
|
||||||
context = {}
|
custom_domain = []
|
||||||
msg_obj = self.pool.get('mail.message')
|
|
||||||
msg_ids = []
|
|
||||||
for user in self.browse(cr, uid, ids, context=context):
|
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,
|
if custom_domain:
|
||||||
limit=limit, offset=offset, context=context)
|
custom_domain += ['|']
|
||||||
if (ascent): msg_ids = self._message_add_ancestor_ids(cr, uid, ids, msg_ids, root_ids, context=context)
|
custom_domain += ['|', ('body_text', 'like', '@%s' % (user.login)), ('body_html', 'like', '@%s' % (user.login))]
|
||||||
return msg_ids
|
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)
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<field name="notification_email_pref"/>
|
<field name="notification_email_pref"/>
|
||||||
</field>
|
</field>
|
||||||
<xpath expr="/form/sheet" position="after">
|
<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"/>
|
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||||
</div>
|
</div>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
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,,1,0,0,0
|
||||||
access_mail_message_all,mail.message.all,model_mail_message,base.group_user,1,1,1,1
|
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_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_subscription_all,mail.subscription.all,model_mail_subscription,,1,1,1,1
|
||||||
access_mail_notification,mail.notification,model_mail_notification,,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
|
access_mail_group,mail.group,model_mail_group,base.group_user,1,1,1,1
|
||||||
|
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
/* ------------------------------ */
|
/* Wall
|
||||||
/* Wall */
|
/* ------------------------------------------------------------ */
|
||||||
/* ------------------------------ */
|
|
||||||
|
|
||||||
.openerp div.oe_mail_wall {
|
.openerp div.oe_mail_wall {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -9,6 +8,17 @@
|
||||||
background: white;
|
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 {
|
.openerp div.oe_mail_wall_action {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
background: #eee;
|
background: #eee;
|
||||||
|
@ -21,36 +31,32 @@
|
||||||
clear: both;
|
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;
|
width: 474px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 2px;
|
||||||
float: right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2 columns view */
|
.openerp ul.oe_mail_wall_threads {
|
||||||
.openerp div.oe_mail_wall_main {
|
|
||||||
float: left;
|
|
||||||
width: 560px;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.openerp div.oe_mail_wall_aside {
|
|
||||||
margin-left: 565px;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Threads */
|
|
||||||
.openerp .oe_mail_wall_threads {
|
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
.openerp .oe_mail_wall_threads textarea {
|
|
||||||
height: 40px;
|
/* Specific display of threads in the wall */
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
|
||||||
|
.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea {
|
||||||
|
width: 434px;
|
||||||
|
height: 30px;
|
||||||
padding: 4px;
|
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;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +70,7 @@
|
||||||
width: 486px;
|
width: 486px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp div.oe_mail_msg_content li{
|
.openerp div.oe_mail_msg_content li {
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
|
@ -79,40 +85,28 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/* RecordThread */
|
/* RecordThread
|
||||||
/* ------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
||||||
.openerp div.oe_mail_recthread {
|
.openerp div.oe_mail_recthread {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Left-side CSS */
|
.openerp div.oe_mail_recthread_main {
|
||||||
.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 {
|
|
||||||
float: left;
|
float: left;
|
||||||
width: 560px;
|
width: 560px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp div.oe_mail_recthread_right {
|
.openerp div.oe_mail_recthread_aside {
|
||||||
float: right;
|
float: right;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.openerp div.oe_mail_recthread_actions {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.openerp div.oe_mail_recthread_actions button {
|
.openerp div.oe_mail_recthread_actions button {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
}
|
}
|
||||||
|
@ -144,16 +138,17 @@
|
||||||
background-image: linear-gradient(to bottom, #dc5f59, #b33630);
|
background-image: linear-gradient(to bottom, #dc5f59, #b33630);
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp textarea.oe_mail_action_textarea {
|
.openerp div.oe_mail_recthread_followers {
|
||||||
height: 60px;
|
margin-bottom: 8px;
|
||||||
padding: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------ */
|
|
||||||
/* ThreadDisplay */
|
/* ------------------------------------------------------------ */
|
||||||
/* ------------------------------ */
|
/* Thread
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
|
||||||
.openerp div.oe_mail_thread_action {
|
.openerp div.oe_mail_thread_action {
|
||||||
|
display: none;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
background: #eee;
|
background: #eee;
|
||||||
|
@ -166,6 +161,26 @@
|
||||||
clear: both;
|
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 {
|
.openerp div.oe_mail_thread_display {
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +189,7 @@
|
||||||
margin-left: 66px;
|
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;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,18 +198,23 @@
|
||||||
border-bottom: 1px solid #D2D9E7;
|
border-bottom: 1px solid #D2D9E7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp .oe_mail_thread_msg:after {
|
.openerp li.oe_mail_thread_msg:after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp .oe_mail_thread_msg > div:after {
|
.openerp li.oe_mail_thread_msg > div:after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.openerp div.oe_mail_msg {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.openerp .oe_mail_msg_notification,
|
.openerp .oe_mail_msg_notification,
|
||||||
.openerp .oe_mail_msg_comment,
|
.openerp .oe_mail_msg_comment,
|
||||||
.openerp .oe_mail_msg_email {
|
.openerp .oe_mail_msg_email {
|
||||||
|
@ -203,11 +223,6 @@
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp .oe_email_icon {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.openerp div.oe_mail_thread_subthread .oe_mail_msg_comment {
|
.openerp div.oe_mail_thread_subthread .oe_mail_msg_comment {
|
||||||
background: #eee;
|
background: #eee;
|
||||||
}
|
}
|
||||||
|
@ -230,9 +245,18 @@
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp img.oe_mail_msg_image {
|
.openerp img.oe_mail_icon {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp img.oe_mail_thumbnail {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp img.oe_mail_frame {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-moz-border-radius: 3px;
|
-moz-border-radius: 3px;
|
||||||
|
@ -247,48 +271,60 @@
|
||||||
clip: rect(5px, 40px, 45px, 0px);
|
clip: rect(5px, 40px, 45px, 0px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------ */
|
.openerp .oe_mail_invisible {
|
||||||
/* Styling (should be openerp) */
|
display: none;
|
||||||
/* ------------------------------ */
|
}
|
||||||
|
|
||||||
.openerp input.oe_mail, textarea.oe_mail {
|
/* ------------------------------------------------------------ */
|
||||||
width: 432px;
|
/* mail.compose.message form view & OpenERP hacks
|
||||||
padding: 4px;
|
/* ------------------------------------------------------------ */
|
||||||
font-size: 12px;
|
|
||||||
|
/* form_view: delete white background */
|
||||||
|
.openerp .oe_mail_msg_content div.oe_formview {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp .oe_mail_msg_content div.oe_form_nosheet {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp .oe_mail_msg_content table.oe_form_group {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* body_html: cleditor */
|
||||||
|
.openerp .oe_mail_msg_content div.cleditorMain {
|
||||||
border: 1px solid #cccccc;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp input.oe_mail:focus, textarea.oe_mail:focus {
|
/* destination_partner_ids */
|
||||||
outline: 0;
|
.openerp .oe_mail_msg_content div.text-core {
|
||||||
border-color: rgba(82, 168, 236, 0.8);
|
height: 22px !important;
|
||||||
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
|
width: 472px;
|
||||||
-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_msg {
|
/* buttons */
|
||||||
padding: 0;
|
.openerp .oe_mail_msg_content .oe_mail_compose_message_icons button.oe_form_button {
|
||||||
margin: 0 0 4px 0;
|
padding: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp .oe_mail_oe_bold {
|
/* ------------------------------------------------------------ */
|
||||||
font-weight: bold;
|
/* Messages layout
|
||||||
}
|
/* ------------------------------------------------------------ */
|
||||||
|
|
||||||
/* ------------------------------ */
|
|
||||||
/* Messages layout */
|
|
||||||
/* ------------------------------ */
|
|
||||||
|
|
||||||
.openerp .oe_mail_msg .oe_mail_msg_title {
|
.openerp .oe_mail_msg .oe_mail_msg_title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -318,6 +354,10 @@
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.openerp .oe_mail_oe_bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read more/less link */
|
/* Read more/less link */
|
||||||
.openerp .oe_mail_msg_content .expand,
|
.openerp .oe_mail_msg_content .expand,
|
||||||
.openerp .oe_mail_msg_content .reduce {
|
.openerp .oe_mail_msg_content .reduce {
|
||||||
|
@ -360,35 +400,3 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style-type: square;
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Before Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 976 B |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 3.4 KiB |
|
@ -1,7 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<template>
|
<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">
|
<table class="oe_view_manager_header">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col width="33%"/>
|
<col width="33%"/>
|
||||||
|
@ -21,30 +25,44 @@
|
||||||
</table>
|
</table>
|
||||||
<div class="oe_mail_wall_main">
|
<div class="oe_mail_wall_main">
|
||||||
<div class="oe_mail_wall_action">
|
<div class="oe_mail_wall_action">
|
||||||
<img class="oe_mail_msg_image oe_left" alt="User img"/>
|
<!-- call the composition form -->
|
||||||
<textarea class="oe_mail oe_mail_wall_action_textarea" placeholder="What are you working on?"/>
|
<t t-call ="mail.compose_message"/>
|
||||||
<button class="oe_right oe_mail_wall_button_comment" type="button">Post comment</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="oe_clear"></div>
|
<div class="oe_clear"/>
|
||||||
<ul class="oe_mail_wall_threads">
|
<ul class="oe_mail_wall_threads">
|
||||||
|
<!-- contains threads -->
|
||||||
</ul>
|
</ul>
|
||||||
<div class="oe_mail_wall_more">
|
<div class="oe_mail_wall_more">
|
||||||
<button class="oe_mail_wall_button_more" type="button">See more discussions</button>
|
<button class="oe_mail_wall_button_more" type="button">See more discussions</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="oe_mail_wall_aside"></div>
|
<div class="oe_mail_wall_aside">
|
||||||
|
<!-- contains currently nothing -->
|
||||||
|
</div>
|
||||||
</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>
|
</t>
|
||||||
|
|
||||||
<div t-name="mail.RecordThread" class="oe_mail_recthread">
|
<!--
|
||||||
<div class="oe_mail_recthread_left"></div>
|
record_thread main template
|
||||||
<div class="oe_mail_recthread_right">
|
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">
|
<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_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_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>
|
||||||
<div class="oe_mail_recthread_followers">
|
<div class="oe_mail_recthread_followers">
|
||||||
<h4>Followers</h4>
|
<h4>Followers</h4>
|
||||||
|
@ -53,44 +71,87 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-name="mail.Thread" class="oe_mail oe_mail_thread">
|
<!--
|
||||||
<div class="oe_mail_thread_action">
|
record_thread.subscriber template
|
||||||
<img class="oe_mail_msg_image oe_left" alt="User img"/>
|
Template used to display a subscriber.
|
||||||
<textarea class="oe_mail oe_mail_action_textarea" placeholder="Add your comment here..." onfocus="this.value = '';"/>
|
-->
|
||||||
|
<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">
|
||||||
|
<!-- contains the composition box (form + image) -->
|
||||||
|
<t t-call="mail.compose_message"/>
|
||||||
|
</div>
|
||||||
|
<div class="oe_mail_thread_display">
|
||||||
|
<!-- contains the threads -->
|
||||||
</div>
|
</div>
|
||||||
<div class="oe_mail_thread_display"></div>
|
|
||||||
<div class="oe_mail_thread_more">
|
<div class="oe_mail_thread_more">
|
||||||
<button class="oe_mail_button_more" type="button">Load more messages</button>
|
<button class="oe_mail_button_more" type="button">Load more messages</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ul>
|
||||||
|
|
||||||
<!-- default layout -->
|
<!-- 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}">
|
<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">
|
<div class="oe_mail_msg_content">
|
||||||
|
<!-- dropdown menu with message options and actions -->
|
||||||
<span class="oe_dropdown_toggle oe_dropdown_arrow">
|
<span class="oe_dropdown_toggle oe_dropdown_arrow">
|
||||||
<ul class="oe_dropdown_menu">
|
<ul class="oe_dropdown_menu">
|
||||||
<t t-if="thread._is_author(record.user_id[0]) && display['show_delete']">
|
<t t-if="display['show_delete']">
|
||||||
<li><a href="#" t-attf-data-id='{record.id}' class="oe_mail_msg_delete">Delete</a></li>
|
<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 t-if="!thread._is_author(record.user_id[0]) && display['show_hide']">
|
<li t-if="display['show_hide']"><a href="#" class="oe_mail_msg_hide" t-attf-data-id='{record.id}'>Remove from Wall</a></li>
|
||||||
<li><a href="#" t-attf-data-id='{record.id}' class="oe_mail_msg_hide">Hide</a></li>
|
<!-- Uncomment when adding subtype hiding
|
||||||
</t>
|
<li t-if="display['show_hide']">
|
||||||
<li t-if="record.type == 'email'"><a href="#" class="oe_mail_msg_details">Details</a></li>
|
<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>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
|
<!-- message itself -->
|
||||||
<div class="oe_mail_msg">
|
<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"/>
|
<t t-raw="record.subject"/>
|
||||||
</h1>
|
</h1>
|
||||||
<div t-if="params.thread_level > 0" class="oe_mail_msg_subtitle">
|
<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>
|
||||||
|
<div class="oe_clear"/>
|
||||||
<div class="oe_mail_msg_body">
|
<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>
|
<a t-attf-href="#model=res.users&id=#{record.user_id[0]}"><t t-raw="record.user_id[1]"/></a>
|
||||||
<t t-raw="record.body"/>
|
<div class="oe_mail_msg_record_body"><t t-raw="record.body"/></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="oe_clear"/>
|
||||||
<ul class="oe_mail_msg_footer">
|
<ul class="oe_mail_msg_footer">
|
||||||
<li><span t-att-title="record.date"><t t-raw="record.timerelative"/></span></li>
|
<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>
|
<li t-if="display['show_reply']"><a href="#" class="oe_mail_msg_reply">Reply</a></li>
|
||||||
|
@ -115,5 +176,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</li>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -33,7 +33,7 @@ from ..mail_message import to_email
|
||||||
# main mako-like expression pattern
|
# main mako-like expression pattern
|
||||||
EXPRESSION_PATTERN = re.compile('(\$\{.+?\})')
|
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
|
"""Generic Email composition wizard. This wizard is meant to be inherited
|
||||||
at model and view level to provide specific wizard features.
|
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:
|
parameters, among which are:
|
||||||
|
|
||||||
* mail.compose.message.mode: if set to 'reply', the wizard is in
|
* mail.compose.message.mode: if set to 'reply', the wizard is in
|
||||||
reply mode and pre-populated with the original quote.
|
reply to a previous message mode and pre-populated with the original
|
||||||
If set to 'mass_mail', the wizard is in mass mailing
|
quote. If set to 'comment', it means you are writing a new message to
|
||||||
where the mail details can contain template placeholders
|
be attached to a document. If set to 'mass_mail', the wizard is in
|
||||||
that will be merged with actual data before being sent
|
mass mailing where the mail details can contain template placeholders
|
||||||
to each recipient. Recipients will be derived from the
|
that will be merged with actual data before being sent to each
|
||||||
records determined via ``context['active_model']`` and
|
recipient.
|
||||||
``context['active_ids']``.
|
|
||||||
* active_model: model name of the document to which the mail being
|
* active_model: model name of the document to which the mail being
|
||||||
composed is related
|
composed is related
|
||||||
* active_id: id of the document to which the mail being composed is
|
* 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'
|
_description = 'Email composition wizard'
|
||||||
|
|
||||||
def default_get(self, cr, uid, fields, context=None):
|
def default_get(self, cr, uid, fields, context=None):
|
||||||
"""Overridden to provide specific defaults depending on the context
|
""" Overridden to provide specific defaults depending on the context
|
||||||
parameters.
|
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
|
:param dict context: several context values will modify the behavior
|
||||||
of the wizard, cfr. the class description.
|
of the wizard, cfr. the class description.
|
||||||
"""
|
"""
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
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)
|
result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)
|
||||||
|
|
||||||
|
# get default values according to the composition mode
|
||||||
vals = {}
|
vals = {}
|
||||||
reply_mode = context.get('mail.compose.message.mode') == 'reply'
|
if compose_mode in ['reply']:
|
||||||
if (not reply_mode) and context.get('active_model') and context.get('active_id'):
|
vals = self.get_message_data(cr, uid, int(context['active_id']), context=context)
|
||||||
# normal mode when sending an email related to any document, as specified by
|
elif compose_mode in ['comment', 'mass_mail'] and active_model and active_id:
|
||||||
# active_model and active_id in context
|
vals = self.get_value(cr, uid, active_model, active_id, 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)
|
|
||||||
for field in vals:
|
for field in vals:
|
||||||
if field in fields:
|
if field in fields:
|
||||||
result.update({field : vals[field]})
|
result[field] = vals[field]
|
||||||
|
|
||||||
# link to model and record if not done yet
|
# link to model and record if not done yet
|
||||||
if not result.get('model') or not result.get('res_id'):
|
if not result.get('model') and active_model:
|
||||||
active_model = context.get('active_model')
|
result['model'] = active_model
|
||||||
res_id = context.get('active_id')
|
if not result.get('res_id') and active_id:
|
||||||
if active_model and active_model not in (self._name, 'mail.message'):
|
result['res_id'] = active_id
|
||||||
result['model'] = active_model
|
|
||||||
if res_id:
|
|
||||||
result['res_id'] = res_id
|
|
||||||
|
|
||||||
# Try to provide default email_from if not specified yet
|
# Try to provide default email_from if not specified yet
|
||||||
if not result.get('email_from'):
|
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
|
result['email_from'] = current_user.user_email or False
|
||||||
return result
|
return result
|
||||||
|
|
||||||
_columns = {
|
_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'),
|
'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"),
|
'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"),
|
||||||
'filter_id': fields.many2one('ir.filters', 'Filters'),
|
'filter_id': fields.many2one('ir.filters', 'Filters'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_value(self, cr, uid, model, res_id, context=None):
|
def get_value(self, cr, uid, model, res_id, context=None):
|
||||||
"""Returns a defaults-like dict with initial values for the composition
|
""" Returns a defaults-like dict with initial values for the composition
|
||||||
wizard when sending an email related to the document record identified
|
wizard when sending an email related to the document record
|
||||||
by ``model`` and ``res_id``.
|
identified by ``model`` and ``res_id``.
|
||||||
|
|
||||||
The default implementation returns an empty dictionary, and is meant
|
The default implementation returns an empty dictionary, and is meant
|
||||||
to be overridden by subclasses.
|
to be overridden by subclasses.
|
||||||
|
|
||||||
:param str model: model name of the document record this mail is related to.
|
:param str model: model name of the document record this mail is
|
||||||
:param int res_id: id of the document record this mail is related to.
|
related to.
|
||||||
:param dict context: several context values will modify the behavior
|
:param int res_id: id of the document record this mail is related to.
|
||||||
of the wizard, cfr. the class description.
|
:param dict context: several context values will modify the behavior
|
||||||
|
of the wizard, cfr. the class description.
|
||||||
"""
|
"""
|
||||||
return {}
|
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
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
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):
|
def get_message_data(self, cr, uid, message_id, context=None):
|
||||||
"""Returns a defaults-like dict with initial values for the composition
|
""" Returns a defaults-like dict with initial values for the composition
|
||||||
wizard when replying to the given message (e.g. including the quote
|
wizard when replying to the given message (e.g. including the quote
|
||||||
of the initial message, and the correct recipient).
|
of the initial message, and the correct recipient). It should not be
|
||||||
Should not be called unless ``context['mail.compose.message.mode'] == 'reply'``.
|
called unless ``context['mail.compose.message.mode'] == 'reply'``.
|
||||||
|
|
||||||
:param int message_id: id of the mail.message to which the user
|
:param int message_id: id of the mail.message to which the user
|
||||||
is replying.
|
is replying.
|
||||||
:param dict context: several context values will modify the behavior
|
:param dict context: several context values will modify the behavior
|
||||||
of the wizard, cfr. the class description.
|
of the wizard, cfr. the class description.
|
||||||
When calling this method, the ``'mail'`` value
|
|
||||||
in the context should be ``'reply'``.
|
|
||||||
"""
|
"""
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
result = {}
|
result = {}
|
||||||
mail_message = self.pool.get('mail.message')
|
if not message_id:
|
||||||
if message_id:
|
return result
|
||||||
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
|
current_user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||||
# new ones later when sending
|
message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context)
|
||||||
'message_id' : message_data.message_id or False,
|
# Form the subject
|
||||||
'references' : message_data.references and tools.ustr(message_data.references) or False,
|
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
|
return result
|
||||||
|
|
||||||
def send_mail(self, cr, uid, ids, context=None):
|
def send_mail(self, cr, uid, ids, context=None):
|
||||||
|
@ -190,76 +249,81 @@ class mail_compose_message(osv.osv_memory):
|
||||||
'''
|
'''
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
mail_message = self.pool.get('mail.message')
|
# composition wizard options
|
||||||
for mail in self.browse(cr, uid, ids, context=context):
|
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 = {}
|
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')
|
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
|
references = None
|
||||||
headers = {}
|
headers = {}
|
||||||
|
body = mail_wiz.body_html if content_subtype == 'html' else mail_wiz.body_text
|
||||||
|
|
||||||
body = mail.body_html if mail.subtype == 'html' else mail.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'):
|
||||||
# Get model, and check whether it is OpenChatter enabled, aka inherit from mail.thread
|
active_ids = context['active_ids']
|
||||||
if context.get('mail.compose.message.mode') == 'mass_mail':
|
active_model = context['active_model']
|
||||||
if context.get('active_ids') and context.get('active_model'):
|
elif mass_mail_mode:
|
||||||
active_ids = context['active_ids']
|
active_model = mail_wiz.model
|
||||||
active_model = context['active_model']
|
active_model_pool = self.pool.get(active_model)
|
||||||
else:
|
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))
|
||||||
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))
|
|
||||||
else:
|
else:
|
||||||
active_model = mail.model
|
active_model = mail_wiz.model
|
||||||
active_ids = [int(mail.res_id)]
|
active_ids = [mail_wiz.res_id]
|
||||||
active_model_pool = self.pool.get(active_model)
|
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 = hasattr(active_model_pool, 'message_append')
|
||||||
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
|
|
||||||
|
|
||||||
if context.get('mail.compose.message.mode') == 'mass_mail':
|
if context.get('mail.compose.message.mode') == 'mass_mail':
|
||||||
# Mass mailing: must render the template patterns
|
# Mass mailing: must render the template patterns
|
||||||
for active_id in active_ids:
|
for active_id in active_ids:
|
||||||
subject = self.render_template(cr, uid, mail.subject, active_model, active_id)
|
rendered_subject = self.render_template(cr, uid, subject, active_model, active_id)
|
||||||
rendered_body = self.render_template(cr, uid, body, active_model, active_id)
|
rendered_body_html = self.render_template(cr, uid, mail_wiz.body_html, active_model, active_id)
|
||||||
email_from = self.render_template(cr, uid, mail.email_from, active_model, active_id)
|
rendered_body_text = self.render_template(cr, uid, mail_wiz.body_text, active_model, active_id)
|
||||||
email_to = self.render_template(cr, uid, mail.email_to, active_model, active_id)
|
email_from = self.render_template(cr, uid, mail_wiz.email_from, active_model, active_id)
|
||||||
email_cc = self.render_template(cr, uid, mail.email_cc, active_model, active_id)
|
email_to = self.render_template(cr, uid, mail_wiz.email_to, active_model, active_id)
|
||||||
email_bcc = self.render_template(cr, uid, mail.email_bcc, active_model, active_id)
|
email_cc = self.render_template(cr, uid, mail_wiz.email_cc, active_model, active_id)
|
||||||
reply_to = self.render_template(cr, uid, mail.reply_to, 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
|
# in mass-mailing mode we only schedule the mail for sending, it will be
|
||||||
# processed as soon as the mail scheduler runs.
|
# processed as soon as the mail scheduler runs.
|
||||||
if mail_thread_enabled:
|
if mail_thread_enabled:
|
||||||
active_model_pool.message_append(cr, uid, [active_id],
|
active_model_pool.message_append(cr, uid, [active_id], rendered_subject, rendered_body_text, rendered_body_html,
|
||||||
subject, body_text=mail.body_text, body_html=mail.body_html, subtype=mail.subtype, state='outgoing',
|
type=type, content_subtype=content_subtype, state=state, partner_ids=partner_ids,
|
||||||
email_to=email_to, email_from=email_from, email_cc=email_cc, email_bcc=email_bcc,
|
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)
|
reply_to=reply_to, references=references, attachments=attachment, headers=headers, context=context)
|
||||||
else:
|
else:
|
||||||
mail_message.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, rendered_body,
|
mail_message_obj.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, rendered_body_text,
|
||||||
model=mail.model, email_cc=to_email(email_cc), email_bcc=to_email(email_bcc), reply_to=reply_to,
|
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,
|
attachments=attachment, references=references, res_id=active_id, partner_ids=partner_ids,
|
||||||
subtype=mail.subtype, headers=headers, context=context)
|
content_subtype=mail_wiz.content_subtype, headers=headers, context=context)
|
||||||
else:
|
else:
|
||||||
# normal mode - no mass-mailing
|
# normal mode - no mass-mailing
|
||||||
if mail_thread_enabled:
|
if mail_thread_enabled:
|
||||||
msg_ids = active_model_pool.message_append(cr, uid, active_ids,
|
msg_ids = active_model_pool.message_append(cr, uid, active_ids, subject, mail_wiz.body_text, mail_wiz.body_html,
|
||||||
mail.subject, body_text=mail.body_text, body_html=mail.body_html, subtype=mail.subtype, state='outgoing',
|
type=type, content_subtype=content_subtype, state=state, partner_ids=partner_ids,
|
||||||
email_to=mail.email_to, email_from=mail.email_from, email_cc=mail.email_cc, email_bcc=mail.email_bcc,
|
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.reply_to, references=references, attachments=attachment, headers=headers, context=context)
|
reply_to=mail_wiz.reply_to, references=references, attachments=attachment, headers=headers, context=context)
|
||||||
else:
|
else:
|
||||||
msg_ids = [mail_message.schedule_with_attach(cr, uid, mail.email_from, to_email(mail.email_to), mail.subject, body,
|
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,
|
||||||
model=mail.model, email_cc=to_email(mail.email_cc), email_bcc=to_email(mail.email_bcc), reply_to=mail.reply_to,
|
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.res_id),
|
attachments=attachment, references=references, res_id=int(mail_wiz.res_id), partner_ids=partner_ids,
|
||||||
subtype=mail.subtype, headers=headers, context=context)]
|
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)
|
# 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'}
|
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)
|
return template and EXPRESSION_PATTERN.sub(merge, template)
|
||||||
|
|
||||||
|
|
||||||
|
class mail_compose_message_extended(osv.TransientModel):
|
||||||
class mail_compose_message_extended(osv.osv_memory):
|
|
||||||
""" Extension of 'mail.compose.message' to support default field values related
|
""" Extension of 'mail.compose.message' to support default field values related
|
||||||
to CRM-like models that follow the following conventions:
|
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'
|
_inherit = 'mail.compose.message'
|
||||||
|
|
||||||
def get_value(self, cr, uid, model, res_id, context=None):
|
def get_value(self, cr, uid, model, res_id, context=None):
|
||||||
"""Overrides the default implementation to provide more default field values
|
""" Overrides the default implementation to provide more default field values
|
||||||
related to the corresponding CRM case.
|
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)
|
model_obj = self.pool.get(model)
|
||||||
if getattr(model_obj, '_mail_compose_message', False) and res_id:
|
if getattr(model_obj, '_mail_compose_message', False) and res_id:
|
||||||
data = model_obj.browse(cr, uid , res_id, context)
|
data = model_obj.browse(cr, uid , res_id, context)
|
||||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
|
||||||
result.update({
|
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_to': data.email_from or False,
|
||||||
'email_cc': tools.ustr(data.email_cc or ''),
|
'email_cc': tools.ustr(data.email_cc or ''),
|
||||||
'subject': data.name or False,
|
'subject': data.name or False,
|
||||||
'body_text': '\n' + tools.ustr(user.signature or ''),
|
|
||||||
'subtype': 'plain',
|
|
||||||
})
|
})
|
||||||
if hasattr(data, 'section_id'):
|
if hasattr(data, 'section_id'):
|
||||||
result['reply_to'] = data.section_id and data.section_id.reply_to or False
|
result['reply_to'] = data.section_id and data.section_id.reply_to or False
|
||||||
return result
|
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:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -37,6 +37,50 @@
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</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">
|
<record id="action_email_compose_message_wizard" model="ir.actions.act_window">
|
||||||
<field name="name">Compose Email</field>
|
<field name="name">Compose Email</field>
|
||||||
<field name="res_model">mail.compose.message</field>
|
<field name="res_model">mail.compose.message</field>
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"website" : "http://www.openerp.com",
|
"website" : "http://www.openerp.com",
|
||||||
"category" : "Manufacturing",
|
"category" : "Manufacturing",
|
||||||
"sequence": 18,
|
"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"],
|
"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"],
|
"depends" : ["procurement", "stock", "resource", "purchase", "product","process"],
|
||||||
"description": """
|
"description": """
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -184,8 +184,8 @@ msgstr "تحذير !"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "ضريبة القيمة المضافة"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -803,6 +803,9 @@ msgstr "جاهز للتصليح"
|
||||||
msgid "No partner !"
|
msgid "No partner !"
|
||||||
msgstr "لا يوجد شريك !"
|
msgstr "لا يوجد شريك !"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "ضريبة القيمة المضافة"
|
||||||
|
|
||||||
#~ msgid "Do you really want to create the invoice(s) ?"
|
#~ msgid "Do you really want to create the invoice(s) ?"
|
||||||
#~ msgstr "هل تريد فعلاً إنشاء الفاتورة/الفواتبر؟"
|
#~ msgstr "هل تريد فعلاً إنشاء الفاتورة/الفواتبر؟"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,8 +169,8 @@ msgstr "Предупреждение!"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "ДДС"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -785,5 +785,8 @@ msgstr "Няма контрагент!"
|
||||||
#~ "Името на обекта трябва да започва с \"x_\" и да не съдържа никакви специални "
|
#~ "Името на обекта трябва да започва с \"x_\" и да не съдържа никакви специални "
|
||||||
#~ "символи!"
|
#~ "символи!"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "ДДС"
|
||||||
|
|
||||||
#~ msgid "Do you really want to create the invoice(s) ?"
|
#~ msgid "Do you really want to create the invoice(s) ?"
|
||||||
#~ msgstr "Искате ли да създадете фактурата/ите"
|
#~ msgstr "Искате ли да създадете фактурата/ите"
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:07+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,7 +169,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -193,8 +193,8 @@ msgstr "Avís!"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "CIF/NIF"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -842,6 +842,9 @@ msgstr "No existeix empresa!"
|
||||||
#~ msgid "Repair Order Ref"
|
#~ msgid "Repair Order Ref"
|
||||||
#~ msgstr "Referència comanda reparació"
|
#~ msgstr "Referència comanda reparació"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "CIF/NIF"
|
||||||
|
|
||||||
#~ msgid "Repairs in quotation"
|
#~ msgid "Repairs in quotation"
|
||||||
#~ msgstr "Reparacions en pressupost"
|
#~ msgstr "Reparacions en pressupost"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,7 +169,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -170,7 +170,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -191,8 +191,8 @@ msgstr "Warnung !"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "USt."
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -926,6 +926,9 @@ msgstr "Kein Partner"
|
||||||
#~ msgid "Do you really want to create the invoice(s) ?"
|
#~ msgid "Do you really want to create the invoice(s) ?"
|
||||||
#~ msgstr "Wollen Sie wirklich die Rechnung(en) erstellen ?"
|
#~ msgstr "Wollen Sie wirklich die Rechnung(en) erstellen ?"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "USt."
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "\n"
|
#~ "\n"
|
||||||
#~ " The aim is to have a complete module to manage all products "
|
#~ " The aim is to have a complete module to manage all products "
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -191,8 +191,8 @@ msgstr "¡Aviso!"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "CIF/NIF"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -870,6 +870,9 @@ msgstr "¡No existe empresa!"
|
||||||
#~ msgid "Make invoices"
|
#~ msgid "Make invoices"
|
||||||
#~ msgstr "Realizar facturas"
|
#~ msgstr "Realizar facturas"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "CIF/NIF"
|
||||||
|
|
||||||
#, python-format
|
#, python-format
|
||||||
#~ msgid "You have to select a partner in the repair form !"
|
#~ msgid "You have to select a partner in the repair form !"
|
||||||
#~ msgstr "¡Debe seleccionar una empresa en el formulario de reparación!"
|
#~ msgstr "¡Debe seleccionar una empresa en el formulario de reparación!"
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,8 +169,8 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "CUIT"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -865,6 +865,9 @@ msgstr "Sin Partner !"
|
||||||
#~ msgid "Repair State"
|
#~ msgid "Repair State"
|
||||||
#~ msgstr "Reparar Estado"
|
#~ msgstr "Reparar Estado"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "CUIT"
|
||||||
|
|
||||||
#~ msgid "Repair Ref"
|
#~ msgid "Repair Ref"
|
||||||
#~ msgstr "Ref de Reparación"
|
#~ msgstr "Ref de Reparación"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
"Language: \n"
|
"Language: \n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
@ -192,8 +192,8 @@ msgstr "¡Aviso!"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "CIF/NIF"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -823,6 +823,9 @@ msgstr "Listo para reparar"
|
||||||
msgid "No partner !"
|
msgid "No partner !"
|
||||||
msgstr "¡No existe empresa!"
|
msgstr "¡No existe empresa!"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "CIF/NIF"
|
||||||
|
|
||||||
#~ msgid "Do you really want to create the invoice(s) ?"
|
#~ msgid "Do you really want to create the invoice(s) ?"
|
||||||
#~ msgstr "¿Desea crear la(s) factura(s)?"
|
#~ msgstr "¿Desea crear la(s) factura(s)?"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,8 +169,8 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "CIF/NIF"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -790,6 +790,9 @@ msgstr ""
|
||||||
#~ msgid "Repair State"
|
#~ msgid "Repair State"
|
||||||
#~ msgstr "Estado de reparación"
|
#~ msgstr "Estado de reparación"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "CIF/NIF"
|
||||||
|
|
||||||
#~ msgid "Repairs in quotation"
|
#~ msgid "Repairs in quotation"
|
||||||
#~ msgstr "Reparaciones en presupuesto"
|
#~ msgstr "Reparaciones en presupuesto"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -174,8 +174,8 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "KM"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -883,3 +883,6 @@ msgstr "Partner puudub!"
|
||||||
|
|
||||||
#~ msgid "Repairs to be invoiced"
|
#~ msgid "Repairs to be invoiced"
|
||||||
#~ msgstr "Remondid arveldamiseks"
|
#~ msgstr "Remondid arveldamiseks"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "KM"
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -174,8 +174,8 @@ msgstr "Varoitus !"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "ALV"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -877,6 +877,9 @@ msgstr "Ei kumppania!"
|
||||||
#~ msgid "Quality"
|
#~ msgid "Quality"
|
||||||
#~ msgstr "Laatu"
|
#~ msgstr "Laatu"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "ALV"
|
||||||
|
|
||||||
#~ msgid "Make invoices"
|
#~ msgid "Make invoices"
|
||||||
#~ msgstr "Luo laskut"
|
#~ msgstr "Luo laskut"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -191,8 +191,8 @@ msgstr "Avertissement !"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "TVA"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -821,6 +821,9 @@ msgstr "Prêt à Réparer"
|
||||||
msgid "No partner !"
|
msgid "No partner !"
|
||||||
msgstr "Pas de partenaire !"
|
msgstr "Pas de partenaire !"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "TVA"
|
||||||
|
|
||||||
#~ msgid "Repairs"
|
#~ msgid "Repairs"
|
||||||
#~ msgstr "Réparations"
|
#~ msgstr "Réparations"
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -170,8 +170,8 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "वैट"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -783,6 +783,9 @@ msgstr ""
|
||||||
#~ msgid "Repair Order Ref"
|
#~ msgid "Repair Order Ref"
|
||||||
#~ msgstr "मरम्मत के आदेश संदर्भ"
|
#~ msgstr "मरम्मत के आदेश संदर्भ"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "वैट"
|
||||||
|
|
||||||
#~ msgid "Repair Fees line"
|
#~ msgid "Repair Fees line"
|
||||||
#~ msgstr "मरम्मत शुल्क लाइन"
|
#~ msgstr "मरम्मत शुल्क लाइन"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -185,8 +185,8 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "PDV"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -806,5 +806,8 @@ msgstr "Spremno za popravak"
|
||||||
msgid "No partner !"
|
msgid "No partner !"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "PDV"
|
||||||
|
|
||||||
#~ msgid "Do you really want to create the invoice(s) ?"
|
#~ msgid "Do you really want to create the invoice(s) ?"
|
||||||
#~ msgstr "Kreirati račun(e) ?"
|
#~ msgstr "Kreirati račun(e) ?"
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=utf-8\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -170,8 +170,8 @@ msgstr "Vigyázat!"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "ÁFA"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -777,5 +777,8 @@ msgstr ""
|
||||||
msgid "No partner !"
|
msgid "No partner !"
|
||||||
msgstr "Nincs partner!"
|
msgstr "Nincs partner!"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "ÁFA"
|
||||||
|
|
||||||
#~ msgid "Do you really want to create the invoice(s) ?"
|
#~ msgid "Do you really want to create the invoice(s) ?"
|
||||||
#~ msgstr "Valóban el akarja készíteni a számlá(ka)t?"
|
#~ msgstr "Valóban el akarja készíteni a számlá(ka)t?"
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,7 +169,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,8 +169,8 @@ msgstr "Attenzione!"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "IVA"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -784,6 +784,9 @@ msgstr "Nessun partner!"
|
||||||
#~ msgid "Repair Order Ref"
|
#~ msgid "Repair Order Ref"
|
||||||
#~ msgstr "Rif. Ordine riparazione"
|
#~ msgstr "Rif. Ordine riparazione"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "IVA"
|
||||||
|
|
||||||
#~ msgid "Invalid XML for View Architecture!"
|
#~ msgid "Invalid XML for View Architecture!"
|
||||||
#~ msgstr "XML non valido per Visualizzazione Architettura!"
|
#~ msgstr "XML non valido per Visualizzazione Architettura!"
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -180,8 +180,8 @@ msgstr "警告"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "消費税"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -793,3 +793,6 @@ msgstr "修理準備完了"
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "No partner !"
|
msgid "No partner !"
|
||||||
msgstr "パートナがありません。"
|
msgstr "パートナがありません。"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "消費税"
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -170,7 +170,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,7 +169,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|
|
@ -154,7 +154,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -191,8 +191,8 @@ msgstr "Waarschuwing !"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "BTW"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -758,7 +758,7 @@ msgstr "Verwijder"
|
||||||
#: field:mrp.repair.fee,product_uom:0
|
#: field:mrp.repair.fee,product_uom:0
|
||||||
#: field:mrp.repair.line,product_uom:0
|
#: field:mrp.repair.line,product_uom:0
|
||||||
msgid "Product UoM"
|
msgid "Product UoM"
|
||||||
msgstr "Produkt UoM"
|
msgstr "Product maateenheid"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: field:mrp.repair,partner_invoice_id:0
|
#: field:mrp.repair,partner_invoice_id:0
|
||||||
|
@ -818,6 +818,9 @@ msgstr "Gereed voor reparatie"
|
||||||
msgid "No partner !"
|
msgid "No partner !"
|
||||||
msgstr "Geen relatie !"
|
msgstr "Geen relatie !"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "BTW"
|
||||||
|
|
||||||
#~ msgid "Invalid XML for View Architecture!"
|
#~ msgid "Invalid XML for View Architecture!"
|
||||||
#~ msgstr "Ongeldige XML voor overzicht"
|
#~ msgstr "Ongeldige XML voor overzicht"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,7 +169,7 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,8 +169,8 @@ msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "VAT"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -788,6 +788,9 @@ msgstr "Brak partnera !"
|
||||||
#~ msgid "Repair State"
|
#~ msgid "Repair State"
|
||||||
#~ msgstr "Stan naprawy"
|
#~ msgstr "Stan naprawy"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "VAT"
|
||||||
|
|
||||||
#~ msgid "Repairs in quotation"
|
#~ msgid "Repairs in quotation"
|
||||||
#~ msgstr "Naprawy w ofercie"
|
#~ msgstr "Naprawy w ofercie"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -190,8 +190,8 @@ msgstr "Atenção!"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr "IVA"
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -828,6 +828,9 @@ msgstr "Nenhum Parceiro !"
|
||||||
#~ msgid "Make invoices"
|
#~ msgid "Make invoices"
|
||||||
#~ msgstr "Criar Facturas"
|
#~ msgstr "Criar Facturas"
|
||||||
|
|
||||||
|
#~ msgid "VAT"
|
||||||
|
#~ msgstr "IVA"
|
||||||
|
|
||||||
#~ msgid "Repairs"
|
#~ msgid "Repairs"
|
||||||
#~ msgstr "Reparações"
|
#~ msgstr "Reparações"
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-14 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2012-07-21 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 15614)\n"
|
"X-Generator: Launchpad (build 15654)\n"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: view:mrp.repair:0
|
#: view:mrp.repair:0
|
||||||
|
@ -169,7 +169,7 @@ msgstr "Atenção !"
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
#: report:repair.order:0
|
#: report:repair.order:0
|
||||||
msgid "VAT"
|
msgid "Tax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_repair
|
#. module: mrp_repair
|
||||||
|
|