[MERGE] Sync with trunk
bzr revid: jam@tinyerp.com-20121018133243-jybuqt2ub9wcm9h7
This commit is contained in:
commit
2ca6464e66
|
@ -514,8 +514,7 @@ class account_move_line(osv.osv):
|
|||
'analytic_lines': fields.one2many('account.analytic.line', 'move_id', 'Analytic lines'),
|
||||
'centralisation': fields.selection([('normal','Normal'),('credit','Credit Centralisation'),('debit','Debit Centralisation'),('currency','Currency Adjustment')], 'Centralisation', size=8),
|
||||
'balance': fields.function(_balance, fnct_search=_balance_search, string='Balance'),
|
||||
'state': fields.selection([('draft','Unbalanced'), ('valid','Valid')], 'Status', readonly=True,
|
||||
help='When new move line is created the state will be \'Draft\'.\n* When all the payments are done it will be in \'Valid\' state.'),
|
||||
'state': fields.selection([('draft','Unbalanced'), ('valid','Balanced')], 'Status', readonly=True),
|
||||
'tax_code_id': fields.many2one('account.tax.code', 'Tax Account', help="The Account can either be a base tax code or a tax code account."),
|
||||
'tax_amount': fields.float('Tax/Base Amount', digits_compute=dp.get_precision('Account'), select=True, help="If the Tax account is a tax code account, this field will contain the taxed amount.If the tax account is base tax code, "\
|
||||
"this field will contain the basic amount(without tax)."),
|
||||
|
|
|
@ -1078,7 +1078,7 @@
|
|||
<field name="currency_id" attrs="{'readonly':[('state','=','valid')]}" groups="base.group_multi_currency"/>
|
||||
<field name="reconcile_partial_id"/>
|
||||
<field name="reconcile_id"/>
|
||||
<field name="state"/>
|
||||
<field name="state" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -1980,7 +1980,7 @@
|
|||
<field name="credit" sum="Total credit"/>
|
||||
<field name="account_tax_id"/>
|
||||
<field name="analytic_account_id" domain="[('parent_id','!=',False)]" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="state"/>
|
||||
<field name="state" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -127,6 +127,7 @@
|
|||
<field name="view_id" ref="account_journal_bank_view"/>
|
||||
<field name="name">Status</field>
|
||||
<field name="field">state</field>
|
||||
<field eval="True" name="invisible"/>
|
||||
<field eval="19" name="sequence"/>
|
||||
</record>
|
||||
<record id="bank_col20" model="account.journal.column">
|
||||
|
@ -214,6 +215,7 @@
|
|||
<field name="view_id" ref="account_journal_bank_view_multi"/>
|
||||
<field name="name">Status</field>
|
||||
<field name="field">state</field>
|
||||
<field eval="True" name="invisible"/>
|
||||
<field eval="19" name="sequence"/>
|
||||
</record>
|
||||
<record id="bank_col20_multi" model="account.journal.column">
|
||||
|
@ -289,6 +291,7 @@
|
|||
<field name="view_id" ref="account_journal_view"/>
|
||||
<field name="name">Status</field>
|
||||
<field name="field">state</field>
|
||||
<field eval="True" name="invisible"/>
|
||||
<field eval="19" name="sequence"/>
|
||||
</record>
|
||||
|
||||
|
@ -370,6 +373,7 @@
|
|||
<field name="view_id" ref="account_sp_journal_view"/>
|
||||
<field name="name">Status</field>
|
||||
<field name="field">state</field>
|
||||
<field eval="True" name="invisible"/>
|
||||
<field eval="19" name="sequence"/>
|
||||
</record>
|
||||
<record id="sp_journal_col20" model="account.journal.column">
|
||||
|
@ -456,6 +460,7 @@
|
|||
<field name="view_id" ref="account_sp_refund_journal_view"/>
|
||||
<field name="name">Status</field>
|
||||
<field name="field">state</field>
|
||||
<field eval="True" name="invisible"/>
|
||||
<field eval="19" name="sequence"/>
|
||||
</record>
|
||||
<record id="sp_refund_journal_col20" model="account.journal.column">
|
||||
|
|
|
@ -16,8 +16,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: account
|
||||
#: view:account.invoice.report:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: account
|
||||
#: view:account.invoice.report:0
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: account
|
||||
#: view:account.invoice.report:0
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -106,8 +106,10 @@ openerp.account = function (instance) {
|
|||
result.context = _.extend(result.context || {}, additional_context);
|
||||
result.flags = result.flags || {};
|
||||
result.flags.new_window = true;
|
||||
return self.do_action(result, function () {
|
||||
self.do_search(self.last_domain, self.last_context, self.last_group_by);
|
||||
return self.do_action(result, {
|
||||
on_close: function () {
|
||||
self.do_search(self.last_domain, self.last_context, self.last_group_by);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-08 01:37+0100\n"
|
||||
"PO-Revision-Date: 2012-04-18 07:47+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"PO-Revision-Date: 2012-10-17 08:25+0000\n"
|
||||
"Last-Translator: filsys <office@filsystem.ro>\n"
|
||||
"Language-Team: Romanian <ro@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-08-28 06:42+0000\n"
|
||||
"X-Generator: Launchpad (build 15864)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: account_asset
|
||||
#: view:account.asset.asset:0
|
||||
|
@ -440,12 +440,12 @@ msgstr "Metoda Timp"
|
|||
#. module: account_asset
|
||||
#: view:account.asset.category:0
|
||||
msgid "Analytic Information"
|
||||
msgstr ""
|
||||
msgstr "Informatii analitice"
|
||||
|
||||
#. module: account_asset
|
||||
#: view:asset.modify:0
|
||||
msgid "Asset Durations to Modify"
|
||||
msgstr ""
|
||||
msgstr "Durata de modificat"
|
||||
|
||||
#. module: account_asset
|
||||
#: constraint:account.move.line:0
|
||||
|
@ -622,7 +622,7 @@ msgstr "Prorata Temporis"
|
|||
#. module: account_asset
|
||||
#: view:account.asset.category:0
|
||||
msgid "Accounting Information"
|
||||
msgstr ""
|
||||
msgstr "Informații contabile"
|
||||
|
||||
#. module: account_asset
|
||||
#: model:ir.model,name:account_asset.model_account_invoice
|
||||
|
|
|
@ -1,3 +1,54 @@
|
|||
.oe_import{
|
||||
display: inline-block;
|
||||
width: 600px;
|
||||
padding: 16px;
|
||||
}
|
||||
.oe_import > p {
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
text-align: justify
|
||||
}
|
||||
|
||||
/* ----------- IMPORT BOX ----------- */
|
||||
|
||||
.oe_import .oe_import_box{
|
||||
margin: 16px;
|
||||
padding: 16px;
|
||||
background: #F0EEEE;
|
||||
border-radius: 3px;
|
||||
border: solid 1px #dddddd;
|
||||
}
|
||||
.oe_import .oe_import_toggle{
|
||||
margin-top: 8px;
|
||||
}
|
||||
.oe_import .oe_import_options{
|
||||
margin-top: 8px;
|
||||
}
|
||||
.oe_import .oe_import_options input{
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.oe_import a.oe_import_toggle {
|
||||
display: block;
|
||||
}
|
||||
.oe_import a.oe_import_toggle:before {
|
||||
content: '+'
|
||||
}
|
||||
|
||||
.oe_import .oe_import_options p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.oe_import .oe_import_options label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height:32px;
|
||||
line-height:32px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* ----------- INITIAL SETUP ------------ */
|
||||
|
||||
.openerp .oe_list_buttons .oe_alternative {
|
||||
visibility: visible;
|
||||
}
|
||||
|
@ -25,38 +76,85 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.oe_import .oe_import_error_report ul .oe_import_report_error {
|
||||
background-color: #FFD9DB;
|
||||
}
|
||||
.oe_import .oe_import_error_report ul .oe_import_report_warning {
|
||||
background-color: #FEFFD9;
|
||||
}
|
||||
.oe_import .oe_import_error_report ul .oe_import_report_info {
|
||||
background-color: #d3ffd3;
|
||||
}
|
||||
|
||||
.oe_import .oe_import_noheaders {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.oe_import a.oe_import_toggle {
|
||||
display: block;
|
||||
}
|
||||
.oe_import a.oe_import_toggle:before {
|
||||
content: '> '
|
||||
}
|
||||
|
||||
.oe_import .oe_import_options p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.oe_import .oe_import_options label {
|
||||
display: inline-block;
|
||||
width: 8em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.oe_import_selector ul,
|
||||
.oe_import_selector li {
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
|
||||
/* ------------- ERRORS AND WARNINGS REPORT ------------ */
|
||||
|
||||
.oe_import .oe_import_error_report ul{
|
||||
padding-left: 16px;
|
||||
}
|
||||
.oe_import .oe_import_report {
|
||||
margin-right: 16px;
|
||||
padding: 4px;
|
||||
list-style: none;
|
||||
border-bottom: solid 1px rgba(0,0,0,0.05);
|
||||
}
|
||||
.oe_import .oe_import_report:first-child{
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
.oe_import .oe_import_report:last-child{
|
||||
border-bottom-left-radius:3px;
|
||||
border-bottom-right-radius:3px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.oe_import .oe_import_report_error {
|
||||
background-color: #FFD9DB;
|
||||
color: #AF5F5E;
|
||||
}
|
||||
.oe_import .oe_import_report_error:last-of-type{
|
||||
border-bottom:none;
|
||||
}
|
||||
.oe_import .oe_import_report_warning {
|
||||
background-color: #FEFFD9;
|
||||
color: #918743;
|
||||
}
|
||||
.oe_import .oe_import_report_warning:last-of-type{
|
||||
border-bottom:none;
|
||||
}
|
||||
.oe_import .oe_import_report_info {
|
||||
background-color: #d3ffd3;
|
||||
}
|
||||
.oe_import .oe_import_report_info:last-of-type{
|
||||
border-bottom:none;
|
||||
}
|
||||
|
||||
/* ------------- THE CSV TABLE ------------ */
|
||||
|
||||
.oe_import .oe_import_grid{
|
||||
margin: 16px;
|
||||
border-radius:3px;
|
||||
border: solid 1px #909090;
|
||||
}
|
||||
.oe_import .oe_import_grid tr{
|
||||
height:16px;
|
||||
}
|
||||
.oe_import .oe_import_grid tr.oe_import_grid-header:first-child{
|
||||
height: 24px;
|
||||
background: #909090;
|
||||
color: white;
|
||||
}
|
||||
.oe_import .oe_import_grid tr:nth-child(odd){
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
.oe_import .oe_import_grid td{
|
||||
padding: 2px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
.oe_import .oe_import_grid tr.oe_import_grid-header td:not(:last-child){
|
||||
border-right: 1px solid #707070;
|
||||
}
|
||||
.oe_import .oe_import_grid td:not(:last-child){
|
||||
border-right: 1px solid #D0D0D0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -53,8 +53,10 @@ openerp.base_import = function (instance) {
|
|||
params: {
|
||||
model: self.dataset.model
|
||||
}
|
||||
}, void 0, void 0, function () {
|
||||
self.reload();
|
||||
}, {
|
||||
on_reverse_breadcrumb: function () {
|
||||
self.reload();
|
||||
},
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -21,24 +21,26 @@
|
|||
class="oe_import_csv" target="_blank">.CSV</a>
|
||||
file to import. If you need a sample importable file, you
|
||||
can use the export tool to generate one.</p>
|
||||
<label t-attf-for="file_#{_id}" autofocus="autofocus">CSV File:</label>
|
||||
<input type="file" id-attf-id="file_#{_id}"
|
||||
name="file" class="oe_import_file"/>
|
||||
<button type="button" class="oe_import_file_reload">
|
||||
<img src="/web/static/src/img/icons/gtk-refresh.png"/>
|
||||
</button>
|
||||
<div class="oe_import_with_file">
|
||||
<a href="#" class="oe_import_toggle">
|
||||
File Format Options…</a>
|
||||
<div class="oe_import_toggled oe_import_options">
|
||||
<p t-foreach="widget.opts" t-as="option">
|
||||
<!-- no @name, avoid submission when file_update called -->
|
||||
<label t-attf-for="#{option.name}_#{_id}">
|
||||
<t t-esc="option.label"/></label>
|
||||
<input t-attf-id="#{option.name}_#{_id}"
|
||||
t-attf-class="oe_import_#{option.name}"
|
||||
t-att-value="option.value"/>
|
||||
</p>
|
||||
<div class="oe_import_box">
|
||||
<label t-attf-for="file_#{_id}" autofocus="autofocus">CSV File:</label>
|
||||
<input type="file" id-attf-id="file_#{_id}"
|
||||
name="file" class="oe_import_file"/>
|
||||
<button type="button" class="oe_import_file_reload">
|
||||
<img src="/web/static/src/img/icons/gtk-refresh.png"/>
|
||||
</button>
|
||||
<div class="oe_import_with_file">
|
||||
<a href="#" class="oe_import_toggle">
|
||||
File Format Options…</a>
|
||||
<div class="oe_import_toggled oe_import_options">
|
||||
<p t-foreach="widget.opts" t-as="option">
|
||||
<!-- no @name, avoid submission when file_update called -->
|
||||
<label t-attf-for="#{option.name}_#{_id}">
|
||||
<t t-esc="option.label"/></label>
|
||||
<input t-attf-id="#{option.name}_#{_id}"
|
||||
t-attf-class="oe_import_#{option.name}"
|
||||
t-att-value="option.value"/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -54,7 +56,7 @@
|
|||
simpler especially when the file has many columns.</p>
|
||||
|
||||
<div class="oe_import_error_report"></div>
|
||||
<table class="oe_import_grid" width="100%"/>
|
||||
<table class="oe_import_grid" />
|
||||
|
||||
<h2>Frequently Asked Questions</h2>
|
||||
<dl>
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: document_page
|
||||
#: field:document.page.type,template:0
|
||||
|
|
|
@ -175,7 +175,7 @@ class email_template(osv.osv):
|
|||
'res_model': 'mail.compose.message',
|
||||
'src_model': src_obj,
|
||||
'view_type': 'form',
|
||||
'context': "{'default_composition_mode': 'mass_mail', 'default_template_id' : %d}" % (template.id),
|
||||
'context': "{'default_composition_mode': 'mass_mail', 'default_template_id' : %d, 'default_use_template': True}" % (template.id),
|
||||
'view_mode':'form,tree',
|
||||
'view_id': res_id,
|
||||
'target': 'new',
|
||||
|
|
|
@ -24,7 +24,8 @@ import tools
|
|||
from osv import osv
|
||||
from osv import fields
|
||||
|
||||
class mail_compose_message(osv.osv_memory):
|
||||
|
||||
class mail_compose_message(osv.TransientModel):
|
||||
_inherit = 'mail.compose.message'
|
||||
|
||||
def _get_templates(self, cr, uid, context=None):
|
||||
|
|
|
@ -8,38 +8,15 @@
|
|||
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//form/footer/button" position="before">
|
||||
<field name="use_template" invisible="1"/>
|
||||
<button icon="gtk-paste" type="object" name="toggle_template"
|
||||
string="" help="Use a message template" invisible="1"/>
|
||||
<button icon="gtk-save" type="object" name="save_as_template"
|
||||
string="" help="Save as a new template" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//form/notebook" position="after">
|
||||
<group attrs="{'invisible':[('use_template','=',False)]}" colspan="4" col="4">
|
||||
<field name="use_template" invisible="1"/>
|
||||
<field name="template_id" colspan="3" invisible="1"
|
||||
<group attrs="{'invisible':[('use_template','=',False)]}">
|
||||
<field name="use_template" invisible="1"
|
||||
on_change="onchange_use_template(use_template, template_id, composition_mode, model, res_id, context)"/>
|
||||
<field name="template_id"
|
||||
on_change="onchange_template_id(use_template, template_id, composition_mode, model, res_id, context)"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="email_compose_message_wizard_inherit_form_chatter">
|
||||
<field name="name">mail.compose.message.form</field>
|
||||
<field name="model">mail.compose.message</field>
|
||||
<field name="inherit_id" ref="mail.email_compose_message_wizard_form_chatter"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='partner_ids']" position="after">
|
||||
<field name="use_template" colspan="2" nolabel="1" invisible="1"
|
||||
on_change="onchange_use_template(use_template, template_id, composition_mode, model, res_id, context)"/>
|
||||
<field name="template_id" colspan="2" nolabel="1"
|
||||
attrs="{'invisible':[('use_template','=',False)]}"
|
||||
on_change="onchange_template_id(use_template, template_id, composition_mode, model, res_id, context)"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='dummy']" position="before">
|
||||
<xpath expr="//form/footer/button" position="after">
|
||||
<button icon="/email_template/static/src/img/email_template.png"
|
||||
type="object" name="toggle_template" string=""
|
||||
help="Use a message template"
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: hr
|
||||
#: model:process.node,name:hr.process_node_openerpuser0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: hr_evaluation
|
||||
#: help:hr_evaluation.plan.phase,send_anonymous_manager:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: hr_holidays
|
||||
#: selection:hr.holidays.status,color_name:0
|
||||
|
|
|
@ -8,15 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-08 01:37+0100\n"
|
||||
"PO-Revision-Date: 2011-06-11 13:23+0000\n"
|
||||
"Last-Translator: Christopher Ormaza - (Ecuadorenlinea.net) "
|
||||
"<chris.ormaza@gmail.com>\n"
|
||||
"PO-Revision-Date: 2012-10-17 04:38+0000\n"
|
||||
"Last-Translator: Cristian Salamea (Gnuthink) <ovnicraft@gmail.com>\n"
|
||||
"Language-Team: Spanish (Ecuador) <es_EC@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-08-28 06:33+0000\n"
|
||||
"X-Generator: Launchpad (build 15864)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: field:hr.payslip.line,condition_select:0
|
||||
|
@ -170,7 +169,7 @@ msgstr "Todas las reglas hijas"
|
|||
#. module: hr_payroll
|
||||
#: view:hr.payslip:0 view:hr.salary.rule:0
|
||||
msgid "Input Data"
|
||||
msgstr ""
|
||||
msgstr "Datos de entrada"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: constraint:hr.payslip:0
|
||||
|
@ -228,7 +227,7 @@ msgstr "Detalle regla salarial"
|
|||
#. module: hr_payroll
|
||||
#: report:paylip.details:0 report:payslip:0
|
||||
msgid "Note"
|
||||
msgstr ""
|
||||
msgstr "Notas"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: field:hr.payroll.structure,code:0 field:hr.payslip,number:0
|
||||
|
@ -271,7 +270,7 @@ msgstr "Suma de el sueldo de todos los contratos actuales del empleado"
|
|||
#. module: hr_payroll
|
||||
#: view:hr.payslip:0
|
||||
msgid "Total Working Days"
|
||||
msgstr ""
|
||||
msgstr "Total de días trabajado"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: help:hr.payslip.line,code:0 help:hr.salary.rule,code:0
|
||||
|
@ -363,7 +362,7 @@ msgstr "Semi-anualmente"
|
|||
#. module: hr_payroll
|
||||
#: view:hr.salary.rule:0
|
||||
msgid "Children Definition"
|
||||
msgstr ""
|
||||
msgstr "Definición de Hijos"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: report:paylip.details:0 report:payslip:0
|
||||
|
@ -373,29 +372,29 @@ msgstr "Correo electrónico"
|
|||
#. module: hr_payroll
|
||||
#: view:hr.payslip.run:0
|
||||
msgid "Search Payslip Batches"
|
||||
msgstr ""
|
||||
msgstr "Buscar bloque de roles"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: field:hr.payslip.line,amount_percentage_base:0
|
||||
#: field:hr.salary.rule,amount_percentage_base:0
|
||||
msgid "Percentage based on"
|
||||
msgstr ""
|
||||
msgstr "Porcentaje basado en"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: help:hr.payslip.line,amount_percentage:0
|
||||
#: help:hr.salary.rule,amount_percentage:0
|
||||
msgid "For example, enter 50.0 to apply a percentage of 50%"
|
||||
msgstr ""
|
||||
msgstr "Por ejemplo, ingrese 50.0 para aplicar el 50%"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: field:hr.payslip,paid:0
|
||||
msgid "Made Payment Order ? "
|
||||
msgstr ""
|
||||
msgstr "Orden de Pago generada ? "
|
||||
|
||||
#. module: hr_payroll
|
||||
#: report:contribution.register.lines:0
|
||||
msgid "PaySlip Lines by Contribution Register"
|
||||
msgstr ""
|
||||
msgstr "Detalle de nómina por contribución registrada"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: help:hr.payslip,state:0
|
||||
|
@ -513,7 +512,7 @@ msgstr "Regla de salario hija"
|
|||
#: field:hr.payslip.run,date_end:0 report:paylip.details:0 report:payslip:0
|
||||
#: field:payslip.lines.contribution.register,date_to:0
|
||||
msgid "Date To"
|
||||
msgstr ""
|
||||
msgstr "Fecha hasta"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: selection:hr.payslip.line,condition_select:0
|
||||
|
@ -576,7 +575,7 @@ msgstr "El contrato al cual aplica este ingreso"
|
|||
#. module: hr_payroll
|
||||
#: view:hr.salary.rule:0
|
||||
msgid "Computation"
|
||||
msgstr ""
|
||||
msgstr "Cálculo"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: help:hr.payslip.input,amount:0
|
||||
|
@ -711,7 +710,7 @@ msgstr "Expresión de python"
|
|||
#. module: hr_payroll
|
||||
#: report:paylip.details:0 report:payslip:0
|
||||
msgid "Designation"
|
||||
msgstr ""
|
||||
msgstr "Designación"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: code:addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py:52
|
||||
|
@ -839,7 +838,7 @@ msgstr "Registrar Nombre"
|
|||
#. module: hr_payroll
|
||||
#: view:hr.salary.rule:0
|
||||
msgid "General"
|
||||
msgstr ""
|
||||
msgstr "General"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: code:addons/hr_payroll/hr_payroll.py:664
|
||||
|
@ -913,7 +912,7 @@ msgstr "Detalle del rol"
|
|||
#: model:ir.actions.act_window,name:hr_payroll.action_view_hr_payslip_form
|
||||
#: model:ir.ui.menu,name:hr_payroll.menu_department_tree
|
||||
msgid "Employee Payslips"
|
||||
msgstr ""
|
||||
msgstr "Roles de Empleado"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: view:hr.payslip.line:0 field:hr.payslip.line,register_id:0
|
||||
|
@ -1004,7 +1003,7 @@ msgstr "Lineas de roles por registro de contribuciones"
|
|||
#. module: hr_payroll
|
||||
#: selection:hr.payslip,state:0
|
||||
msgid "Waiting"
|
||||
msgstr ""
|
||||
msgstr "Esperando"
|
||||
|
||||
#. module: hr_payroll
|
||||
#: report:paylip.details:0 report:payslip:0
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: l10n_be
|
||||
#: field:partner.vat.intra,test_xml:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: l10n_be_hr_payroll
|
||||
#: help:hr.employee,disabled_spouse_bool:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: l10n_cn
|
||||
#: model:account.account.type,name:l10n_cn.user_type_profit_and_loss
|
||||
|
|
|
@ -8,24 +8,24 @@ msgstr ""
|
|||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2011-12-23 09:56+0000\n"
|
||||
"PO-Revision-Date: 2011-04-11 18:21+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"PO-Revision-Date: 2012-10-17 04:39+0000\n"
|
||||
"Last-Translator: Cristian Salamea (Gnuthink) <ovnicraft@gmail.com>\n"
|
||||
"Language-Team: Spanish (Ecuador) <es_EC@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-08-28 06:42+0000\n"
|
||||
"X-Generator: Launchpad (build 15864)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_expense
|
||||
msgid "Gasto"
|
||||
msgstr ""
|
||||
msgstr "Gasto"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_stock
|
||||
msgid "Inventario"
|
||||
msgstr ""
|
||||
msgstr "Inventario"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:ir.actions.todo,note:l10n_ec.config_call_account_template_ec
|
||||
|
@ -50,47 +50,47 @@ msgstr ""
|
|||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_receivable
|
||||
msgid "Por Cobrar"
|
||||
msgstr ""
|
||||
msgstr "Por Cobrar"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_asset
|
||||
msgid "Activo"
|
||||
msgstr ""
|
||||
msgstr "Activo"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_tax
|
||||
msgid "Impuesto"
|
||||
msgstr ""
|
||||
msgstr "Impuesto"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_liability
|
||||
msgid "Pasivo"
|
||||
msgstr ""
|
||||
msgstr "Pasivo"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_capital
|
||||
msgid "Capital"
|
||||
msgstr ""
|
||||
msgstr "Capital"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_cash
|
||||
msgid "Efectivo"
|
||||
msgstr ""
|
||||
msgstr "Efectivo"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_payable
|
||||
msgid "Por Pagar"
|
||||
msgstr ""
|
||||
msgstr "Por Pagar"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_income
|
||||
msgid "Ingreso"
|
||||
msgstr ""
|
||||
msgstr "Ingreso"
|
||||
|
||||
#. module: l10n_ec
|
||||
#: model:account.account.type,name:l10n_ec.account_type_view
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
msgstr "Vista"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: l10n_ma
|
||||
#: model:account.account.type,name:l10n_ma.cpt_type_imm
|
||||
|
|
|
@ -69,29 +69,29 @@
|
|||
"51800_advertising","51800","Merchant Account Fees","other","l10n_us.user_type_cogs","cost_of_goods_sold","FALSE","l10n_us.account_chart_template_advertising"
|
||||
"53500_advertising","53500","Subcontracted Services","other","l10n_us.user_type_cogs","cost_of_goods_sold","FALSE","l10n_us.account_chart_template_advertising"
|
||||
"64200_advertising","64200","Marketing Expense","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_advertising"
|
||||
"41200_agriculture","41200","Agricultural Program Payments","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"42500_agriculture","42500","Commodity Credit Loans","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"42800_agriculture","42800","Cooperative Distributions","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"42900_agriculture","42900","Crop Insurance Proceeds","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"43000_agriculture","43000","Crop Sales","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"43100_agriculture","43100","Custom Hire Income","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"43600_agriculture","43600","Farmers Market Sales","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"44100_agriculture","44100","Fuel Tax Credits and Other Inc.","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"45500_agriculture","45500","Livestock Sales","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"47400_agriculture","47400","Rental Income","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"61100_agriculture","61100","Car and Truck Expenses","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"61500_agriculture","61500","Chemicals Purchased","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"61900_agriculture","61900","Conservation Expenses","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"62300_agriculture","62300","Custom Hire and Contract Labor","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"62900_agriculture","62900","Feed Purchased","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"63000_agriculture","63000","Fertilizers and Lime","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"63100_agriculture","63100","Freight and Trucking","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"63200_agriculture","63200","Gasoline, Fuel and Oil","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"67500_agriculture","67500","Seeds and Plants Purchased","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"67800_agriculture","67800","Small Tools and Equipment","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"67900_agriculture","67900","Storage and Warehousing","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"68500_agriculture","68500","Uniforms","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"68800_agriculture","68800","Veterinary, Breeding, Medicine","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agricultar"
|
||||
"41200_agriculture","41200","Agricultural Program Payments","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"42500_agriculture","42500","Commodity Credit Loans","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"42800_agriculture","42800","Cooperative Distributions","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"42900_agriculture","42900","Crop Insurance Proceeds","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"43000_agriculture","43000","Crop Sales","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"43100_agriculture","43100","Custom Hire Income","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"43600_agriculture","43600","Farmers Market Sales","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"44100_agriculture","44100","Fuel Tax Credits and Other Inc.","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"45500_agriculture","45500","Livestock Sales","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"47400_agriculture","47400","Rental Income","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"61100_agriculture","61100","Car and Truck Expenses","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"61500_agriculture","61500","Chemicals Purchased","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"61900_agriculture","61900","Conservation Expenses","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"62300_agriculture","62300","Custom Hire and Contract Labor","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"62900_agriculture","62900","Feed Purchased","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"63000_agriculture","63000","Fertilizers and Lime","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"63100_agriculture","63100","Freight and Trucking","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"63200_agriculture","63200","Gasoline, Fuel and Oil","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"67500_agriculture","67500","Seeds and Plants Purchased","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"67800_agriculture","67800","Small Tools and Equipment","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"67900_agriculture","67900","Storage and Warehousing","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"68500_agriculture","68500","Uniforms","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"68800_agriculture","68800","Veterinary, Breeding, Medicine","other","l10n_us.user_type_expense","expense","FALSE","l10n_us.account_chart_template_agriculture"
|
||||
"24600_construction_trades","24600","Customer Deposits Received","other","l10n_us.user_type_other_current_liability","other_current_liability","FALSE","l10n_us.account_chart_template_construction"
|
||||
"45100_construction_trades","45100","Job Income","other","l10n_us.user_type_income","income","FALSE","l10n_us.account_chart_template_construction"
|
||||
"50800_construction_trades","50800","Commissions Paid","other","l10n_us.user_type_cogs","cost_of_goods_sold","FALSE","l10n_us.account_chart_template_construction"
|
||||
|
|
|
|
@ -19,8 +19,8 @@
|
|||
<field name="complete_tax_set" eval="False" />
|
||||
<field name="parent_id" ref="account_chart_template_cogs"/>
|
||||
</record>
|
||||
<record id="account_chart_template_agricultar" model="account.chart.template">
|
||||
<field name="name">Agricultar</field>
|
||||
<record id="account_chart_template_agriculture" model="account.chart.template">
|
||||
<field name="name">Agriculture</field>
|
||||
<field name="complete_tax_set" eval="False" />
|
||||
<field name="parent_id" ref="account_chart_template_basic"/>
|
||||
</record>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<field name="bank_account_view_id" ref="cash_expenditure"/>
|
||||
</record>
|
||||
|
||||
<record id="account_chart_template_agricultar" model="account.chart.template">
|
||||
<record id="account_chart_template_agriculture" model="account.chart.template">
|
||||
<field name="bank_account_view_id" ref="cash_expenditure"/>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: lunch
|
||||
#: view:lunch.cashbox.clean:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,subtype:0 field:mail.message,subtype:0
|
||||
|
|
|
@ -84,14 +84,20 @@ class mail_notification(osv.Model):
|
|||
return False
|
||||
|
||||
def set_message_read(self, cr, uid, msg_ids, read=None, context=None):
|
||||
if msg_ids == None:
|
||||
return False
|
||||
if type(msg_ids) is not list:
|
||||
msg_ids=[msg_ids]
|
||||
""" TDE note: add a comment, verify method calls, because js seems obfuscated. """
|
||||
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
notif_ids = self.search(cr, uid, [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)], context=context)
|
||||
|
||||
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
|
||||
notif_ids = self.search(cr, uid, [('partner_id', '=', partner_id), ('message_id', 'in', msg_ids)], context=context)
|
||||
# all message have notifications: already set them as (un)read
|
||||
if len(notif_ids) == len(msg_ids):
|
||||
return self.write(cr, uid, notif_ids, {'read': read}, context=context)
|
||||
|
||||
# some messages do not have notifications: find which one, create notification, update read status
|
||||
exist_notification = dict.fromkeys(msg_ids, False)
|
||||
for notification in self.browse(cr, uid, notif_ids, context=context):
|
||||
exist_notification[notification.message_id] = True
|
||||
for msg_id in exist_notification.keys():
|
||||
self.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context)
|
||||
return self.write(cr, uid, notif_ids, {'read': read}, context=context)
|
||||
|
||||
def get_partners_to_notify(self, cr, uid, message, context=None):
|
||||
|
|
|
@ -25,12 +25,13 @@ from osv import osv
|
|||
from osv import fields
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
|
||||
class mail_group(osv.Model):
|
||||
""" A mail_group is a collection of users sharing messages in a discussion
|
||||
group. The group mechanics are based on the followers. """
|
||||
_description = 'Discussion group'
|
||||
_name = 'mail.group'
|
||||
_mail_autothread = False
|
||||
_mail_flat_thread = False
|
||||
_inherit = ['mail.thread']
|
||||
_inherits = {'mail.alias': 'alias_id', 'ir.ui.menu': 'menu_id'}
|
||||
|
||||
|
|
|
@ -57,19 +57,19 @@ class mail_message(osv.Model):
|
|||
|
||||
def _get_record_name(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Return the related document name, using get_name. """
|
||||
result = dict.fromkeys(ids, '')
|
||||
for message in self.browse(cr, uid, ids, context=context):
|
||||
if not message.model or not message.res_id:
|
||||
result = dict.fromkeys(ids, False)
|
||||
for message in self.read(cr, uid, ids, ['model', 'res_id'], context=context):
|
||||
if not message['model'] or not message['res_id']:
|
||||
continue
|
||||
try:
|
||||
result[message.id] = self._shorten_name(self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1])
|
||||
result[message['id']] = self._shorten_name(self.pool.get(message['model']).name_get(cr, uid, [message['res_id']], context=context)[0][1])
|
||||
except (orm.except_orm, osv.except_osv):
|
||||
pass
|
||||
return result
|
||||
|
||||
def _get_unread(self, cr, uid, ids, name, arg, context=None):
|
||||
def _get_to_read(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Compute if the message is unread by the current user. """
|
||||
res = dict((id, {'unread': False}) for id in ids)
|
||||
res = dict((id, {'to_read': False}) for id in ids)
|
||||
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
notif_obj = self.pool.get('mail.notification')
|
||||
notif_ids = notif_obj.search(cr, uid, [
|
||||
|
@ -78,11 +78,11 @@ class mail_message(osv.Model):
|
|||
('read', '=', False)
|
||||
], context=context)
|
||||
for notif in notif_obj.browse(cr, uid, notif_ids, context=context):
|
||||
res[notif.message_id.id]['unread'] = True
|
||||
res[notif.message_id.id]['to_read'] = True
|
||||
return res
|
||||
|
||||
def _search_unread(self, cr, uid, obj, name, domain, context=None):
|
||||
""" Search for messages unread by the current user. Condition is
|
||||
def _search_to_read(self, cr, uid, obj, name, domain, context=None):
|
||||
""" Search for messages to read by the current user. Condition is
|
||||
inversed because we search unread message on a read column. """
|
||||
if domain[0][2]:
|
||||
read_cond = '(read = false or read is null)'
|
||||
|
@ -128,18 +128,17 @@ class mail_message(osv.Model):
|
|||
'date': fields.datetime('Date'),
|
||||
'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1),
|
||||
'body': fields.html('Contents', help='Automatically sanitized HTML contents'),
|
||||
'unread': fields.function(_get_unread, fnct_search=_search_unread,
|
||||
type='boolean', string='Unread',
|
||||
help='Functional field to search for unread messages linked to uid'),
|
||||
'to_read': fields.function(_get_to_read, fnct_search=_search_to_read,
|
||||
type='boolean', string='To read',
|
||||
help='Functional field to search for messages the current user has to read'),
|
||||
'subtype_id': fields.many2one('mail.message.subtype', 'Subtype'),
|
||||
'vote_user_ids': fields.many2many('res.users', 'mail_vote', 'message_id', 'user_id', string='Votes',
|
||||
help='Users that voted for this message'),
|
||||
'is_private': fields.boolean('Private message'),
|
||||
}
|
||||
|
||||
def _needaction_domain_get(self, cr, uid, context=None):
|
||||
if self._needaction:
|
||||
return [('unread', '=', True)]
|
||||
return [('to_read', '=', True)]
|
||||
return []
|
||||
|
||||
def _get_default_author(self, cr, uid, context=None):
|
||||
|
@ -151,7 +150,6 @@ class mail_message(osv.Model):
|
|||
'date': lambda *a: fields.datetime.now(),
|
||||
'author_id': lambda self, cr, uid, ctx={}: self._get_default_author(cr, uid, ctx),
|
||||
'body': '',
|
||||
'is_private': True,
|
||||
}
|
||||
|
||||
#------------------------------------------------------
|
||||
|
@ -176,180 +174,197 @@ class mail_message(osv.Model):
|
|||
# Message loading for web interface
|
||||
#------------------------------------------------------
|
||||
|
||||
def _message_dict_get(self, cr, uid, msg, context=None):
|
||||
""" Return a dict representation of the message browse record. A read
|
||||
is performed to because of access rights issues (reading many2one
|
||||
fields allow to have the foreign record name without having
|
||||
to check external access rights).
|
||||
def _message_get_dict(self, cr, uid, message, context=None):
|
||||
""" Return a dict representation of the message.
|
||||
|
||||
:param dict message: read result of a mail.message
|
||||
"""
|
||||
has_voted = False
|
||||
vote_ids = self.pool.get('res.users').name_get(cr, SUPERUSER_ID, [user.id for user in msg.vote_user_ids], context=context)
|
||||
for vote in vote_ids:
|
||||
if vote[0] == uid:
|
||||
has_voted = True
|
||||
break
|
||||
if uid in message['vote_user_ids']:
|
||||
has_voted = True
|
||||
else:
|
||||
has_voted = False
|
||||
|
||||
try:
|
||||
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context)]
|
||||
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, message['attachment_ids'], context=context)]
|
||||
except (orm.except_orm, osv.except_osv):
|
||||
attachment_ids = []
|
||||
|
||||
try:
|
||||
author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0]
|
||||
is_author = uid == msg.author_id.user_ids[0].id
|
||||
except Exception:
|
||||
author_id = False
|
||||
is_author = False
|
||||
try:
|
||||
partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context)
|
||||
partner_ids = self.pool.get('res.partner').name_get(cr, uid, message['partner_ids'], context=context)
|
||||
except (orm.except_orm, osv.except_osv):
|
||||
partner_ids = []
|
||||
|
||||
return {
|
||||
'id': msg.id,
|
||||
'type': msg.type,
|
||||
'id': message['id'],
|
||||
'type': message['type'],
|
||||
'attachment_ids': attachment_ids,
|
||||
'body': msg.body,
|
||||
'model': msg.model,
|
||||
'res_id': msg.res_id,
|
||||
'record_name': msg.record_name,
|
||||
'subject': msg.subject,
|
||||
'date': msg.date,
|
||||
'author_id': author_id,
|
||||
'is_author': is_author,
|
||||
'body': message['body'],
|
||||
'model': message['model'],
|
||||
'res_id': message['res_id'],
|
||||
'record_name': message['record_name'],
|
||||
'subject': message['subject'],
|
||||
'date': message['date'],
|
||||
'author_id': message['author_id'],
|
||||
'is_author': message['author_id'] and message['author_id'][0] == uid,
|
||||
'partner_ids': partner_ids,
|
||||
'parent_id': msg.parent_id and msg.parent_id.id or False,
|
||||
'vote_user_ids': vote_ids,
|
||||
'parent_id': message['parent_id'] and message['parent_id'][0] or False,
|
||||
# TDE note: see with CHM about votes, how they are displayed (only number, or name_get ?)
|
||||
# 'vote_user_ids': vote_ids,
|
||||
'has_voted': has_voted,
|
||||
'unread': msg.unread and msg.unread['unread'] or False
|
||||
'to_read': message['to_read'],
|
||||
}
|
||||
|
||||
def _message_read_expandable(self, cr, uid, tree, result, message_loaded, domain, context, parent_id, limit):
|
||||
"""
|
||||
create the expandable message for all parent message read
|
||||
this function is used by message_read
|
||||
"""
|
||||
""" Create the expandable message for all parent message read
|
||||
this function is used by message_read
|
||||
TDE note: add default values for args, add some comments
|
||||
|
||||
tree_not = []
|
||||
:param dict tree: tree of message ids
|
||||
"""
|
||||
tree_not = []
|
||||
# expandable for not show message
|
||||
for id_msg in tree:
|
||||
for msg_id in tree:
|
||||
# get all childs
|
||||
not_loaded_ids = self.search(cr, SUPERUSER_ID, [['parent_id','=',id_msg],['id','not in',message_loaded]], None, limit=1000)
|
||||
not_loaded_ids = self.search(cr, SUPERUSER_ID, [
|
||||
('parent_id', '=', msg_id),
|
||||
('id', 'not in', message_loaded)
|
||||
], context=context, limit=1000)
|
||||
# group childs not read
|
||||
id_min=None
|
||||
id_max=None
|
||||
nb=0
|
||||
id_min = None
|
||||
id_max = None
|
||||
nb = 0
|
||||
for not_loaded_id in not_loaded_ids:
|
||||
if not_loaded_id not in tree:
|
||||
nb+=1
|
||||
if id_min==None or id_min>not_loaded_id:
|
||||
id_min=not_loaded_id
|
||||
if id_max==None or id_max<not_loaded_id:
|
||||
id_max=not_loaded_id
|
||||
nb += 1
|
||||
if id_min == None or id_min > not_loaded_id:
|
||||
id_min = not_loaded_id
|
||||
if id_max == None or id_max < not_loaded_id:
|
||||
id_max = not_loaded_id
|
||||
tree_not.append(not_loaded_id)
|
||||
else:
|
||||
if nb>0:
|
||||
if nb > 0:
|
||||
result.append({
|
||||
'domain': [['id','>=',id_min],['id','<=',id_max],['parent_id','=',id_msg]],
|
||||
'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', msg_id)],
|
||||
'nb_messages': nb,
|
||||
'type': 'expandable',
|
||||
'parent_id': id_msg,
|
||||
'id': id_min
|
||||
'type': 'expandable',
|
||||
'parent_id': msg_id,
|
||||
'id': id_min,
|
||||
})
|
||||
id_min=None
|
||||
id_max=None
|
||||
nb=0
|
||||
if nb>0:
|
||||
id_min = None
|
||||
id_max = None
|
||||
nb = 0
|
||||
if nb > 0:
|
||||
result.append({
|
||||
'domain': [['id','>=',id_min],['id','<=',id_max],['parent_id','=',id_msg]],
|
||||
'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', msg_id)],
|
||||
'nb_messages': nb,
|
||||
'type': 'expandable',
|
||||
'parent_id': id_msg,
|
||||
'type': 'expandable',
|
||||
'parent_id': msg_id,
|
||||
'id': id_min
|
||||
})
|
||||
|
||||
|
||||
# expandable for limit max
|
||||
ids = self.search(cr, SUPERUSER_ID, domain+[['id','not in',message_loaded+tree+tree_not]], context=context, limit=1)
|
||||
ids = self.search(cr, SUPERUSER_ID, domain + [('id', 'not in', message_loaded + tree + tree_not)], context=context, limit=1)
|
||||
if len(ids) > 0:
|
||||
result.append(
|
||||
{
|
||||
result.append({
|
||||
'domain': domain,
|
||||
'nb_messages': 0,
|
||||
'type': 'expandable',
|
||||
'parent_id': parent_id,
|
||||
'type': 'expandable',
|
||||
'parent_id': parent_id,
|
||||
'id': -1
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
result = sorted(result, key=lambda k: k['id'])
|
||||
|
||||
return result
|
||||
|
||||
def message_read(self, cr, uid, ids=False, domain=[], level=0, context=None, parent_id=False, limit=None):
|
||||
_message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read',
|
||||
'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name']
|
||||
|
||||
def _get_parent(self, cr, uid, message, context=None):
|
||||
""" Tools method that tries to get the parent of a mail.message. If
|
||||
no parent, or if uid has no access right on the parent, False
|
||||
is returned.
|
||||
|
||||
:param dict message: read result of a mail.message
|
||||
"""
|
||||
if not message['parent_id']:
|
||||
return False
|
||||
parent_id = message['parent_id'][0]
|
||||
try:
|
||||
return self.read(cr, uid, parent_id, self._message_read_fields, context=context)
|
||||
except (orm.except_orm, osv.except_osv):
|
||||
return False
|
||||
|
||||
def message_read(self, cr, uid, ids=False, domain=[], context=None, parent_id=False, limit=None):
|
||||
""" Read messages from mail.message, and get back a structured tree
|
||||
of messages to be displayed as discussion threads. If IDs is set,
|
||||
fetch these records. Otherwise use the domain to fetch messages.
|
||||
After having fetch messages, their parents will be added to obtain
|
||||
well formed threads.
|
||||
|
||||
TDE note: update this comment after final method implementation
|
||||
|
||||
:param domain: optional domain for searching ids
|
||||
:param limit: number of messages to fetch
|
||||
:param parent_id: if parent_id reached, stop searching for
|
||||
further parents
|
||||
:return list: list of trees of messages
|
||||
"""
|
||||
message_loaded = context and context.get('message_loaded') or [0]
|
||||
|
||||
# don't read the message display by .js, in context message_loaded list
|
||||
if context and context.get('message_loaded'):
|
||||
domain += [ ['id','not in',message_loaded] ];
|
||||
|
||||
# TDE note: use an argument, do not use context
|
||||
if context is None:
|
||||
context = {}
|
||||
if context.get('message_loaded'):
|
||||
domain += [('id', 'not in', context.get('message_loaded'))]
|
||||
limit = limit or self._message_read_limit
|
||||
context = context or {}
|
||||
|
||||
tree = []
|
||||
result = []
|
||||
id_tree = []
|
||||
message_list = []
|
||||
record = None
|
||||
|
||||
# select ids
|
||||
if ids and ids!=[None]:
|
||||
for msg in self.browse(cr, uid, ids, context=context):
|
||||
result.append(self._message_dict_get(cr, uid, msg, context=context))
|
||||
return result
|
||||
# TDE note: should not receive [None] -> investigate
|
||||
if ids and ids != [None]:
|
||||
for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
|
||||
message_list.append(self._message_get_dict(cr, uid, message, context=context))
|
||||
return message_list
|
||||
|
||||
# key: ID, value: record
|
||||
ids = self.search(cr, SUPERUSER_ID, domain, context=context, limit=limit)
|
||||
for msg in self.browse(cr, uid, ids, context=context):
|
||||
|
||||
for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
|
||||
# if not in record and not in message_loded list
|
||||
if msg.id not in tree and msg.id not in message_loaded :
|
||||
record = self._message_dict_get(cr, uid, msg, context=context)
|
||||
tree.append(msg.id)
|
||||
result.append(record)
|
||||
if message['id'] not in id_tree and message['id'] not in context.get('message_loaded', []):
|
||||
record = self._message_get_dict(cr, uid, message, context=context)
|
||||
id_tree.append(message['id'])
|
||||
message_list.append(record)
|
||||
|
||||
while msg.parent_id and msg.parent_id.id != parent_id:
|
||||
parent_id = msg.parent_id.id
|
||||
if msg.parent_id.id not in tree:
|
||||
msg = msg.parent_id
|
||||
tree.append(msg.id)
|
||||
parent = self._get_parent(cr, uid, message, context=context)
|
||||
while parent and parent['id'] != parent_id:
|
||||
if parent['id'] not in id_tree:
|
||||
message = parent
|
||||
id_tree.append(message['id'])
|
||||
# if not in record and not in message_loded list
|
||||
if msg.id not in message_loaded :
|
||||
record = self._message_dict_get(cr, uid, msg, context=context)
|
||||
result.append(record)
|
||||
if message['id'] not in context.get('message_loaded', []):
|
||||
record = self._message_get_dict(cr, uid, message, context=context)
|
||||
message_list.append(record)
|
||||
parent = self._get_parent(cr, uid, parent, context=context)
|
||||
|
||||
result = sorted(result, key=lambda k: k['id'])
|
||||
message_list = sorted(message_list, key=lambda k: k['id'])
|
||||
|
||||
result = self._message_read_expandable(cr, uid, tree, result, message_loaded, domain, context, parent_id, limit)
|
||||
message_list = self._message_read_expandable(cr, uid, id_tree, message_list, context.get('message_loaded', []), domain, context, parent_id, limit)
|
||||
|
||||
return result
|
||||
return message_list
|
||||
|
||||
def user_free_attachment(self, cr, uid, context=None):
|
||||
attachment_list = []
|
||||
# TDE Note: do we need this ?
|
||||
# def user_free_attachment(self, cr, uid, context=None):
|
||||
# attachment_list = []
|
||||
|
||||
attachment = self.pool.get('ir.attachment')
|
||||
attachment_ids = attachment.search(cr, uid, [('res_model','=',''),('create_uid','=',uid)])
|
||||
if len(attachment_ids):
|
||||
attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)]
|
||||
# attachment = self.pool.get('ir.attachment')
|
||||
# attachment_ids = attachment.search(cr, uid, [('res_model','=',''),('create_uid','=',uid)])
|
||||
# if len(attachment_ids):
|
||||
# attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)]
|
||||
|
||||
return attachment_list
|
||||
# return attachment_list
|
||||
|
||||
#------------------------------------------------------
|
||||
# Email api
|
||||
|
@ -502,19 +517,17 @@ class mail_message(osv.Model):
|
|||
missing_notified = missing_notified
|
||||
if missing_notified:
|
||||
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(4, p_id) for p_id in missing_notified]}, context=context)
|
||||
partners_to_notify |= extra_notified
|
||||
|
||||
def _notify(self, cr, uid, newid, context=None):
|
||||
""" Add the related record followers to the destination partner_ids if is not a private message.
|
||||
Call mail_notification.notify to manage the email sending
|
||||
"""
|
||||
message = self.browse(cr, uid, newid, context=context)
|
||||
if message and (message.is_private!=False and message.is_private!=None):
|
||||
if message and message.model and message.res_id:
|
||||
self._notify_followers(cr, uid, newid, message, context=context)
|
||||
|
||||
# add myself if I wrote on my wall,
|
||||
# unless remove myself author
|
||||
if ((message.model=="res.partner" and message.res_id==message.author_id.id)):
|
||||
|
||||
# add myself if I wrote on my wall, otherwise remove myself author
|
||||
if ((message.model == "res.partner" and message.res_id == message.author_id.id)):
|
||||
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(4, message.author_id.id)]}, context=context)
|
||||
else:
|
||||
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(3, message.author_id.id)]}, context=context)
|
||||
|
|
|
@ -57,8 +57,8 @@
|
|||
<field name="type"/>
|
||||
<field name="author_id"/>
|
||||
<filter string="Unread"
|
||||
name="unread_message" help="Show unread message"
|
||||
domain="[('unread', '=', True)]"/>
|
||||
name="messages_unread" help="Show messages to read"
|
||||
domain="[('to_read', '=', True)]"/>
|
||||
<filter string="Comments"
|
||||
name="comments" help="Comments"
|
||||
domain="[('type', '=', 'comment')]"/>
|
||||
|
@ -78,7 +78,7 @@
|
|||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_message_search"/>
|
||||
<field name="context">{'search_default_unread_message':True}</field>
|
||||
<field name="context">{'search_default_to_read_message':True}</field>
|
||||
</record>
|
||||
|
||||
<!-- Add menu entry in Settings/Email -->
|
||||
|
@ -87,22 +87,22 @@
|
|||
<record id="action_mail_inbox_feeds" model="ir.actions.client">
|
||||
<field name="name">Inbox</field>
|
||||
<field name="tag">mail.wall</field>
|
||||
<field name="params" eval=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]),('unread', '=', True)],
|
||||
'context': {'default_model': 'res.partner'} }""/>
|
||||
<field name="params" eval=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', True)],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid} }""/>
|
||||
</record>
|
||||
|
||||
<record id="action_mail_archives_feeds" model="ir.actions.client">
|
||||
<field name="name">Archives</field>
|
||||
<field name="tag">mail.wall</field>
|
||||
<field name="params" eval=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]),('unread', '=', False)],
|
||||
'context': {'default_model': 'res.partner'} }""/>
|
||||
<field name="params" eval=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', False)],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid} }""/>
|
||||
</record>
|
||||
|
||||
<record id="action_mail_sent_feeds" model="ir.actions.client">
|
||||
<field name="name">Sent</field>
|
||||
<field name="tag">mail.wall</field>
|
||||
<field name="params" eval=""{'domain': [('author_id.user_ids', 'in', [uid])],
|
||||
'context': {'default_model': 'res.partner'} }""/>
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid} }""/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -36,6 +36,7 @@ from tools.safe_eval import safe_eval as eval
|
|||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def decode_header(message, header, separator=' '):
|
||||
return separator.join(map(decode, message.get_all(header, [])))
|
||||
|
||||
|
@ -57,10 +58,16 @@ class mail_thread(osv.AbstractModel):
|
|||
to override at least the ``message_new`` and ``message_update``
|
||||
methods (calling ``super``) to add model-specific behavior at
|
||||
creation and update of a thread when processing incoming emails.
|
||||
|
||||
Options:
|
||||
- _mail_flat_thread: if set to True, all messages without parent_id
|
||||
are automatically attached to the first message posted on the
|
||||
ressource. If set to False, the display of Chatter is done using
|
||||
threads, and no parent_id is automatically set.
|
||||
'''
|
||||
_name = 'mail.thread'
|
||||
_description = 'Email Thread'
|
||||
_mail_autothread = True
|
||||
_mail_flat_thread = True
|
||||
|
||||
def _get_message_data(self, cr, uid, ids, name, args, context=None):
|
||||
""" Computes:
|
||||
|
@ -84,13 +91,12 @@ class mail_thread(osv.AbstractModel):
|
|||
res[thread.id]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def _get_subscription_data(self, cr, uid, ids, name, args, context=None):
|
||||
""" Computes:
|
||||
- message_is_follower: is uid in the document followers
|
||||
- message_subtype_data: data about document subtypes: which are
|
||||
available, which are followed if any """
|
||||
res = dict((id, dict(message_subtype_data='', message_is_follower=False)) for id in ids)
|
||||
res = dict((id, dict(message_subtype_data='')) for id in ids)
|
||||
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
|
||||
# find current model subtypes, add them to a dictionary
|
||||
|
@ -109,11 +115,10 @@ class mail_thread(osv.AbstractModel):
|
|||
], context=context)
|
||||
for fol in fol_obj.browse(cr, uid, fol_ids, context=context):
|
||||
thread_subtype_dict = res[fol.res_id]['message_subtype_data']
|
||||
res[fol.res_id]['message_is_follower'] = True
|
||||
for subtype in fol.subtype_ids:
|
||||
thread_subtype_dict[subtype.name]['followed'] = True
|
||||
res[fol.res_id]['message_subtype_data'] = thread_subtype_dict
|
||||
|
||||
|
||||
return res
|
||||
|
||||
def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None):
|
||||
|
@ -132,9 +137,12 @@ class mail_thread(osv.AbstractModel):
|
|||
def _get_followers(self, cr, uid, ids, name, arg, context=None):
|
||||
fol_obj = self.pool.get('mail.followers')
|
||||
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids)])
|
||||
res = dict((res_id, []) for res_id in ids)
|
||||
res = dict((id, dict(message_follower_ids=[], message_is_follower=False)) for id in ids)
|
||||
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
for fol in fol_obj.browse(cr, SUPERUSER_ID, fol_ids):
|
||||
res[fol.res_id].append(fol.partner_id.id)
|
||||
res[fol.res_id]['message_follower_ids'].append(fol.partner_id.id)
|
||||
if fol.partner_id.id == user_pid:
|
||||
res[fol.res_id]['message_is_follower'] = True
|
||||
return res
|
||||
|
||||
def _set_followers(self, cr, uid, id, name, value, arg, context=None):
|
||||
|
@ -188,15 +196,11 @@ class mail_thread(osv.AbstractModel):
|
|||
return res
|
||||
|
||||
_columns = {
|
||||
'message_is_follower': fields.function(_get_subscription_data,
|
||||
type='boolean', string='Is a Follower', multi='_get_subscription_data,'),
|
||||
'message_subtype_data': fields.function(_get_subscription_data,
|
||||
type='text', string='Subscription data', multi="_get_subscription_data",
|
||||
help="Holds data about the subtypes. The content of this field "\
|
||||
"is a structure holding the current model subtypes, and the "\
|
||||
"current document followed subtypes."),
|
||||
'message_is_follower': fields.function(_get_followers,
|
||||
type='boolean', string='Is a Follower', multi='_get_followers,'),
|
||||
'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers,
|
||||
fnct_search=_search_followers, type='many2many', obj='res.partner', string='Followers'),
|
||||
fnct_search=_search_followers, type='many2many',
|
||||
obj='res.partner', string='Followers', multi='_get_followers'),
|
||||
'message_comment_ids': fields.one2many('mail.message', 'res_id',
|
||||
domain=lambda self: [('model', '=', self._name), ('type', 'in', ('comment', 'email'))],
|
||||
string='Comments and emails',
|
||||
|
@ -398,7 +402,8 @@ class mail_thread(osv.AbstractModel):
|
|||
overrides the automatic detection based on the message
|
||||
headers.
|
||||
"""
|
||||
if context is None: context = {}
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
# 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
|
||||
|
@ -414,7 +419,8 @@ class mail_thread(osv.AbstractModel):
|
|||
thread_id, custom_values,
|
||||
context=context)
|
||||
msg = self.message_parse(cr, uid, msg_txt, save_original=save_original, context=context)
|
||||
if strip_attachments: msg.pop('attachments', None)
|
||||
if strip_attachments:
|
||||
msg.pop('attachments', None)
|
||||
thread_id = False
|
||||
for model, thread_id, custom_values, user_id in routes:
|
||||
if self._name != model:
|
||||
|
@ -633,8 +639,10 @@ class mail_thread(osv.AbstractModel):
|
|||
(isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), "Invalid thread_id"
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id and thread_id[0]
|
||||
mail_message = self.pool.get('mail.message')
|
||||
model = context.get('thread_model', self._name) if thread_id else False
|
||||
|
||||
attachment_ids=[]
|
||||
attachment_ids = []
|
||||
for name, content in attachments:
|
||||
if isinstance(content, unicode):
|
||||
content = content.encode('utf-8')
|
||||
|
@ -648,24 +656,20 @@ class mail_thread(osv.AbstractModel):
|
|||
}
|
||||
attachment_ids.append((0, 0, data_attach))
|
||||
|
||||
# get subtype
|
||||
if not subtype:
|
||||
subtype = 'mail.mt_comment'
|
||||
s = subtype.split('.')
|
||||
if len(s)==1:
|
||||
s = ('mail', s[0])
|
||||
ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, s[0], s[1])
|
||||
subtype_id = ref and ref[1] or False
|
||||
|
||||
model = context.get('thread_model', self._name) if thread_id else False
|
||||
messages = self.pool.get('mail.message')
|
||||
|
||||
#auto link messages for same id and object
|
||||
if self._mail_autothread and thread_id:
|
||||
message_ids = messages.search(cr, uid, ['&',('res_id', '=', thread_id),('model','=',model)], context=context)
|
||||
if len(message_ids):
|
||||
parent_id = min(message_ids)
|
||||
# fetch subtype
|
||||
if subtype:
|
||||
s_data = subtype.split('.')
|
||||
if len(s_data) == 1:
|
||||
s_data = ('mail', s_data[0])
|
||||
ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, s_data[0], s_data[1])
|
||||
subtype_id = ref and ref[1] or False
|
||||
else:
|
||||
subtype_id = False
|
||||
|
||||
# _mail_flat_thread: automatically set free messages to the first posted message
|
||||
if self._mail_flat_thread and not parent_id and thread_id:
|
||||
message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)], context=context, order="id ASC", limit=1)
|
||||
parent_id = message_ids and message_ids[0] or False
|
||||
|
||||
values = kwargs
|
||||
values.update({
|
||||
|
@ -679,53 +683,47 @@ class mail_thread(osv.AbstractModel):
|
|||
'subtype_id': subtype_id,
|
||||
})
|
||||
|
||||
# if the parent is private, the message must be private
|
||||
if parent_id:
|
||||
msg = messages.browse(cr, uid, parent_id, context=context)
|
||||
if msg.is_private:
|
||||
values["is_private"] = msg.is_private
|
||||
|
||||
# Avoid warnings about non-existing fields
|
||||
for x in ('from', 'to', 'cc'):
|
||||
values.pop(x, None)
|
||||
|
||||
return messages.create(cr, uid, values, context=context)
|
||||
return mail_message.create(cr, uid, values, context=context)
|
||||
|
||||
def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification',
|
||||
subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
|
||||
# TDE FIXME: body is plaintext: convert it into html
|
||||
# when writing on res.partner, without specific thread_id -> redirect to the user's partner
|
||||
if self._name == 'res.partner' and not thread_id:
|
||||
thread_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
|
||||
new_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type=type,
|
||||
subtype=subtype, parent_id=parent_id, context=context)
|
||||
|
||||
# Chatter: attachments linked to the document (not done JS-side), load the message
|
||||
if attachments:
|
||||
ir_attachment = self.pool.get('ir.attachment')
|
||||
mail_message = self.pool.get('mail.message')
|
||||
attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [('res_model', '=', False), ('res_id', '=', False), ('user_id', '=', uid), ('id', 'in', attachments)], context=context)
|
||||
if attachment_ids:
|
||||
ir_attachment.write(cr, SUPERUSER_ID, attachment_ids, {'res_model': self._name, 'res_id': thread_id}, context=context)
|
||||
mail_message.write(cr, SUPERUSER_ID, [new_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]}, context=context)
|
||||
new_message = self.pool.get('mail.message').message_read(cr, uid, [new_message_id], context=context)
|
||||
|
||||
return new_message
|
||||
|
||||
#------------------------------------------------------
|
||||
# Followers API
|
||||
#------------------------------------------------------
|
||||
|
||||
def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification',
|
||||
subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
|
||||
# if the user write on his wall
|
||||
if self._name=='res.partner' and not thread_id:
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
thread_id = user.partner_id.id
|
||||
|
||||
added_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type=type,
|
||||
subtype=subtype, parent_id=parent_id, context=context)
|
||||
|
||||
attachment_ids=[]
|
||||
if attachments:
|
||||
ir_attachment = self.pool.get('ir.attachment')
|
||||
attachment_ids = ir_attachment.search(cr, 1, [('res_model', '=', ""), ('res_id', '=', ""), ('user_id', '=', uid), ('id', 'in', attachments)], context=context)
|
||||
if attachment_ids:
|
||||
self.pool.get('ir.attachment').write(cr, 1, attachment_ids, { 'res_model': self._name, 'res_id': thread_id }, context=context)
|
||||
self.pool.get('mail.message').write(cr, 1, [added_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]} )
|
||||
|
||||
added_message = self.pool.get('mail.message').message_read(cr, uid, [added_message_id])
|
||||
return added_message
|
||||
|
||||
def get_message_subtypes(self, cr, uid, ids, context=None):
|
||||
""" message_subtype_data: data about document subtypes: which are
|
||||
available, which are followed if any """
|
||||
def message_get_subscription_data(self, cr, uid, ids, context=None):
|
||||
""" Wrapper to get subtypes. """
|
||||
return self._get_subscription_data(cr, uid, ids, None, None, context=context)
|
||||
|
||||
def message_subscribe_users(self, cr, uid, ids, user_ids=None, subtype_ids=None, context=None):
|
||||
""" Wrapper on message_subscribe, using users. If user_ids is not
|
||||
provided, subscribe uid instead. """
|
||||
if not user_ids:
|
||||
return False
|
||||
if user_ids is None:
|
||||
user_ids = [uid]
|
||||
partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)]
|
||||
return self.message_subscribe(cr, uid, ids, partner_ids, subtype_ids=subtype_ids, context=context)
|
||||
|
||||
|
@ -738,14 +736,14 @@ class mail_thread(osv.AbstractModel):
|
|||
subtype_ids = subtype_obj.search(cr, uid, [('default', '=', True), '|', ('res_model', '=', self._name), ('res_model', '=', False)], context=context)
|
||||
# update the subscriptions
|
||||
fol_obj = self.pool.get('mail.followers')
|
||||
fol_ids = fol_obj.search(cr, 1, [('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)], context=context)
|
||||
fol_obj.write(cr, 1, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
|
||||
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)], context=context)
|
||||
fol_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
|
||||
return True
|
||||
|
||||
def message_unsubscribe_users(self, cr, uid, ids, user_ids=None, context=None):
|
||||
""" Wrapper on message_subscribe, using users. If user_ids is not
|
||||
provided, unsubscribe uid instead. """
|
||||
if not user_ids:
|
||||
if user_ids is None:
|
||||
user_ids = [uid]
|
||||
partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)]
|
||||
return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context)
|
||||
|
|
|
@ -25,7 +25,7 @@ class res_partner_mail(osv.Model):
|
|||
""" Update partner to add a field about notification preferences """
|
||||
_name = "res.partner"
|
||||
_inherit = ['res.partner', 'mail.thread']
|
||||
_mail_autothread = False
|
||||
_mail_flat_thread = False
|
||||
|
||||
_columns = {
|
||||
'notification_email_send': fields.selection([
|
||||
|
|
|
@ -99,7 +99,8 @@ class res_users(osv.Model):
|
|||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
# User alias is sync'ed with login
|
||||
if vals.get('login'): vals['alias_name'] = vals['login']
|
||||
if vals.get('login'):
|
||||
vals['alias_name'] = vals['login']
|
||||
return super(res_users, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
|
@ -110,14 +111,20 @@ class res_users(osv.Model):
|
|||
alias_pool.unlink(cr, uid, alias_ids, context=context)
|
||||
return res
|
||||
|
||||
def message_post(self, cr, uid, thread_id, context=None, **kwargs):
|
||||
def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification',
|
||||
subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
|
||||
""" Redirect the posting of message on res.users to the related partner.
|
||||
This is done because when giving the context of Chatter on the
|
||||
various mailboxes, we do not have access to the current partner_id.
|
||||
We therefore post on the user and redirect on its partner. """
|
||||
assert thread_id, "res.users does not support posting global messages"
|
||||
if context and 'thread_model' in context:
|
||||
context['thread_model'] = 'res.partner'
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id[0]
|
||||
partner_id = self.pool.get('res.users').browse(cr, uid, thread_id).partner_id.id
|
||||
return self.pool.get('res.partner').message_post(cr, uid, partner_id, context=context, **kwargs)
|
||||
partner_id = self.pool.get('res.users').read(cr, uid, thread_id, ['partner_id'], context=context)['partner_id'][0]
|
||||
return self.pool.get('res.partner').message_post_api(cr, uid, partner_id, body=body, subject=subject,
|
||||
type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, **kwargs)
|
||||
|
||||
def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
|
||||
partner_id = self.pool.get('res.users').browse(cr, uid, ids)[0].partner_id.id
|
||||
|
|
|
@ -3,7 +3,7 @@ access_mail_message_all,mail.message.all,model_mail_message,,1,0,1,0
|
|||
access_mail_message_group_user,mail.message.group.user,model_mail_message,base.group_user,1,1,1,1
|
||||
access_mail_mail_all,mail.mail.all,model_mail_mail,,0,0,1,0
|
||||
access_mail_mail_system,mail.mail.system,model_mail_mail,base.group_system,1,1,1,1
|
||||
access_mail_followers_all,mail.followers.all,model_mail_followers,,0,0,0,0
|
||||
access_mail_followers_all,mail.followers.all,model_mail_followers,,1,0,0,0
|
||||
access_mail_followers_system,mail.followers.system,model_mail_followers,base.group_system,1,1,1,1
|
||||
access_mail_notification_all,mail.notification.all,model_mail_notification,,1,0,0,0
|
||||
access_mail_notification_aystem,mail.notification.system,model_mail_notification,base.group_system,1,1,1,1
|
||||
|
|
|
|
@ -3,16 +3,23 @@
|
|||
<data>
|
||||
|
||||
<!-- RULES -->
|
||||
<record id="group_rule_public_and_joined" model="ir.rule">
|
||||
<record id="mail_group_public_and_joined" model="ir.rule">
|
||||
<field name="name">Mail.group: access only public and joined groups</field>
|
||||
<field name="model_id" ref="model_mail_group"/>
|
||||
<!-- This rule has to be improved for employee only groups -->
|
||||
<field name="domain_force">['|', '|', ('public', '=', 'public'), ('message_follower_ids', 'in', [user.partner_id.id]), '&', ('public','=','groups'), ('group_public_id','in', [g.id for g in user.groups_id])]</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_followers_read_own" model="ir.rule">
|
||||
<field name="name">mail.followers: read its own entries</field>
|
||||
<field name="model_id" ref="model_mail_followers"/>
|
||||
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_unlink" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
This rule can not be uncommented, because we have a more wide method in mail.message. When we implement a many2one_variable field, we will be able to uncomment this.
|
||||
|
||||
<record id="mail_message_read_partner_or_author" model="ir.rule">
|
||||
<field name="name">mail.message: read if notified or author</field>
|
||||
<field name="model_id" ref="model_mail_message"/>
|
||||
|
|
|
@ -143,12 +143,12 @@
|
|||
/* subtypes
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_mouse_subtypes {
|
||||
.openerp .oe_mail_subtypes {
|
||||
display:inline-block;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
}
|
||||
.openerp .oe_mouse_subtypes .oe_recthread_subtypes {
|
||||
.openerp .oe_mail_subtypes .oe_recthread_subtypes {
|
||||
background: #fff;
|
||||
padding: 2px;
|
||||
border: 1px solid #aaaaaa;
|
||||
|
@ -156,10 +156,10 @@
|
|||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
.openerp .oe_mouse_subtypes.oe_mouseout .oe_recthread_subtypes {
|
||||
.openerp .oe_mail_subtypes.oe_mouseout .oe_recthread_subtypes {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_mouse_subtypes.oe_mouseover .oe_recthread_subtypes {
|
||||
.openerp .oe_mail_subtypes.oe_mouseover .oe_recthread_subtypes {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ openerp.mail = function(session) {
|
|||
*/
|
||||
|
||||
session.web.FormView = session.web.FormView.extend({
|
||||
do_action: function(action, on_close) {
|
||||
do_action: function(action) {
|
||||
if (action.res_model == 'mail.compose.message') {
|
||||
|
||||
/* hack for stop context propagation of wrong value
|
||||
|
@ -25,8 +25,8 @@ openerp.mail = function(session) {
|
|||
*/
|
||||
for(var key in action.context){
|
||||
if( key!='default_template_id' &&
|
||||
key!='default_composition_mode' &&
|
||||
key!='default_use_template' &&
|
||||
key!='default_is_private' &&
|
||||
key!='default_model' &&
|
||||
key!='default_res_id' &&
|
||||
key!='default_subtype' &&
|
||||
|
@ -44,7 +44,7 @@ openerp.mail = function(session) {
|
|||
$('.openerp .oe_mail_wall_threads .oe_mail_thread button.oe_mail_wall_button_fetch').click();
|
||||
|
||||
}
|
||||
return this._super(action, on_close);
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -131,7 +131,6 @@ openerp.mail = function(session) {
|
|||
this.id = options.parameters.id;
|
||||
this.model = options.parameters.model;
|
||||
this.res_id = options.parameters.res_id;
|
||||
this.is_private = options.parameters.is_private;
|
||||
this.partner_ids = options.parameters.partner_ids;
|
||||
this.options={thread:{}};
|
||||
this.options.thread.show_header_compose = options.parameters.options.thread.show_header_compose;
|
||||
|
@ -299,22 +298,23 @@ openerp.mail = function(session) {
|
|||
|
||||
/* to avoid having unsorted file on the server.
|
||||
we will show the users files of the first message post
|
||||
TDE note: unnecessary call to server I think
|
||||
*/
|
||||
set_free_attachments: function(){
|
||||
var self=this;
|
||||
this.parent_thread.ds_message.call('user_free_attachment').then(function(attachments){
|
||||
this.attachment_ids=[];
|
||||
for(var i in attachments){
|
||||
self.attachment_ids[i]={
|
||||
'id': attachments[i].id,
|
||||
'name': attachments[i].name,
|
||||
'filename': attachments[i].filename,
|
||||
'url': mail.ChatterUtils.get_attachment_url(self.session, attachments[i])
|
||||
};
|
||||
}
|
||||
self.display_attachments();
|
||||
});
|
||||
},
|
||||
// set_free_attachments: function(){
|
||||
// var self=this;
|
||||
// this.parent_thread.ds_message.call('user_free_attachment').then(function(attachments){
|
||||
// this.attachment_ids=[];
|
||||
// for(var i in attachments){
|
||||
// self.attachment_ids[i]={
|
||||
// 'id': attachments[i].id,
|
||||
// 'name': attachments[i].name,
|
||||
// 'filename': attachments[i].filename,
|
||||
// 'url': mail.ChatterUtils.get_attachment_url(self.session, attachments[i])
|
||||
// };
|
||||
// }
|
||||
// self.display_attachments();
|
||||
// });
|
||||
// },
|
||||
|
||||
bind_events: function() {
|
||||
var self = this;
|
||||
|
@ -340,13 +340,12 @@ openerp.mail = function(session) {
|
|||
views: [[false, 'form']],
|
||||
target: 'new',
|
||||
context: {
|
||||
'default_res_model': this.context.default_res_model,
|
||||
'default_res_id': this.context.default_res_id,
|
||||
'default_model': false,
|
||||
'default_res_id': 0,
|
||||
'default_content_subtype': 'html',
|
||||
'default_is_private': true,
|
||||
'default_parent_id': this.id,
|
||||
'default_body': mail.ChatterUtils.get_text2html(this.$('textarea').val() || ''),
|
||||
'default_attachment_ids': attachments
|
||||
'default_attachment_ids': attachments,
|
||||
},
|
||||
};
|
||||
this.do_action(action);
|
||||
|
@ -387,7 +386,7 @@ openerp.mail = function(session) {
|
|||
mail.ChatterUtils.get_text2html(body),
|
||||
false,
|
||||
'comment',
|
||||
false,
|
||||
'mail.mt_comment',,
|
||||
this.context.default_parent_id,
|
||||
attachments]
|
||||
).then(this.parent_thread.proxy('switch_new_message'));
|
||||
|
@ -520,7 +519,7 @@ openerp.mail = function(session) {
|
|||
|
||||
this.vote_user_ids = param.vote_user_ids || [];
|
||||
|
||||
this.unread = param.unread || false;
|
||||
this.to_read = param.to_read || false;
|
||||
this._date = param.date;
|
||||
this.author_id = param.author_id || [];
|
||||
this.attachment_ids = param.attachment_ids || [];
|
||||
|
@ -680,10 +679,10 @@ openerp.mail = function(session) {
|
|||
var ids = [this.id].concat( this.get_child_ids() );
|
||||
|
||||
if($(event.srcElement).hasClass("oe_read")) {
|
||||
this.ds_notification.call('set_message_read', [ids,true]);
|
||||
this.ds_notification.call('set_message_read', [ids, true]);
|
||||
this.$el.removeClass("oe_mail_unread").addClass("oe_mail_read");
|
||||
} else {
|
||||
this.ds_notification.call('set_message_read', [ids,false]);
|
||||
this.ds_notification.call('set_message_read', [ids, false]);
|
||||
this.$el.removeClass("oe_mail_read").addClass("oe_mail_unread");
|
||||
}
|
||||
return false;
|
||||
|
@ -830,7 +829,6 @@ openerp.mail = function(session) {
|
|||
this.id= param.id || false;
|
||||
this.model= param.model || false;
|
||||
this.parent_id= param.parent_id || false;
|
||||
this.is_private = param.is_private || false;
|
||||
this.author_id = param.author_id || false;
|
||||
this.partner_ids = [];
|
||||
for(var i in param.partner_ids){
|
||||
|
@ -866,7 +864,7 @@ openerp.mail = function(session) {
|
|||
return display_done && compose_done;
|
||||
},
|
||||
|
||||
instantiate_ComposeMessage: function(){
|
||||
instantiate_ComposeMessage: function() {
|
||||
// add message composition form view
|
||||
this.ComposeMessage = new mail.ThreadComposeMessage(this,{
|
||||
'context': this.context,
|
||||
|
@ -1014,7 +1012,7 @@ openerp.mail = function(session) {
|
|||
fetch_context = replace_context ? replace_context : this.context;
|
||||
fetch_context.message_loaded= [this.id||0].concat( self.options.thread._parents[0].get_child_ids() );
|
||||
|
||||
return this.ds_message.call('message_read', [ids, fetch_domain, fetch_context, 0, this.context.default_parent_id || undefined]
|
||||
return this.ds_message.call('message_read', [ids, fetch_domain, fetch_context, this.context.default_parent_id || undefined]
|
||||
).then(this.proxy('switch_new_message'));
|
||||
},
|
||||
|
||||
|
@ -1167,6 +1165,7 @@ openerp.mail = function(session) {
|
|||
// update domain
|
||||
var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]);
|
||||
// create and render Thread widget
|
||||
// TDE note: replace message_is_follower by a check in message_follower_ids, as message_is_follower is not used in views anymore
|
||||
var show_header_compose = this.view.is_action_enabled('edit') ||
|
||||
(this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
|
||||
|
||||
|
@ -1243,7 +1242,7 @@ openerp.mail = function(session) {
|
|||
var self = this;
|
||||
this.searchview = new session.web.SearchView(this, this.ds_msg, false, defaults || {}, hidden || false);
|
||||
return this.searchview.appendTo(this.$('.oe_view_manager_view_search')).then(function () {
|
||||
self.searchview.on_search.add(self.do_searchview_search);
|
||||
self.searchview.on('search_data', self, self.do_searchview_search);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -26,12 +26,11 @@ openerp_mail_followers = function(session, mail) {
|
|||
this._super.apply(this, arguments);
|
||||
this.options.image = this.node.attrs.image || 'image_small';
|
||||
this.options.title = this.node.attrs.title || 'Followers';
|
||||
this.options.context = this.node.attrs.context;
|
||||
this.options.comment = this.node.attrs.help || false;
|
||||
this.options.displayed_nb = this.node.attrs.displayed_nb || 10;
|
||||
this.ds_model = new session.web.DataSetSearch(this, this.view.model);
|
||||
this.sub_model = new session.web.DataSetSearch(this,'mail.message.subtype');
|
||||
this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
|
||||
this.follower_model = new session.web.DataSetSearch(this,'mail.followers');
|
||||
this.ds_users = new session.web.DataSetSearch(this, 'res.users');
|
||||
},
|
||||
|
||||
start: function() {
|
||||
|
@ -40,7 +39,6 @@ openerp_mail_followers = function(session, mail) {
|
|||
this._check_visibility();
|
||||
this.reinit();
|
||||
this.bind_events();
|
||||
this.display_subtypes();
|
||||
},
|
||||
|
||||
_check_visibility: function() {
|
||||
|
@ -54,17 +52,17 @@ openerp_mail_followers = function(session, mail) {
|
|||
|
||||
bind_events: function() {
|
||||
var self = this;
|
||||
this.$('button.oe_follower')
|
||||
.on('click', function () {
|
||||
if($(this).hasClass('oe_notfollow'))
|
||||
self.do_follow();
|
||||
else
|
||||
self.do_unfollow();
|
||||
});
|
||||
|
||||
this.$el.on('click', 'ul.oe_subtypes input', self.do_update_subscription );
|
||||
|
||||
this.$el.on('click', 'button.oe_invite', function(event) {
|
||||
// event: click on '(Un)Follow' button, that toggles the follow for uid
|
||||
this.$('.oe_follower').on('click', function (event) {
|
||||
if($(this).hasClass('oe_notfollow'))
|
||||
self.do_follow();
|
||||
else
|
||||
self.do_unfollow();
|
||||
});
|
||||
// event: click on a subtype, that (un)subscribe for this subtype
|
||||
this.$el.on('click', 'ul.oe_subtypes input', self.do_update_subscription);
|
||||
// event: click on 'invite' button, that opens the invite wizard
|
||||
this.$('.oe_invite').on('click', function (event) {
|
||||
action = {
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'mail.wizard.invite',
|
||||
|
@ -77,69 +75,90 @@ openerp_mail_followers = function(session, mail) {
|
|||
'default_res_id': self.view.datarecord.id
|
||||
},
|
||||
}
|
||||
self.do_action(action, function() { self.read_value(); });
|
||||
self.do_action(action, {
|
||||
on_close: function() {
|
||||
self.read_value();
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
read_value: function() {
|
||||
read_value: function () {
|
||||
var self = this;
|
||||
return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).pipe(function (results) {
|
||||
self.set_value(results[0].message_follower_ids);
|
||||
});
|
||||
},
|
||||
|
||||
render_value: function() {
|
||||
set_value: function (value_) {
|
||||
this._super(value_);
|
||||
// TDE FIXME: render_value is never called... ask to niv
|
||||
this.render_value();
|
||||
},
|
||||
|
||||
render_value: function () {
|
||||
this.reinit();
|
||||
return this.fetch_followers(this.get("value"));
|
||||
},
|
||||
|
||||
set_is_follower: function(value_) {
|
||||
for(var i in value_){
|
||||
if(value_[i]['user_ids'][0]==this.session.uid)
|
||||
this.message_is_follower=true;
|
||||
this.display_buttons();
|
||||
return true;
|
||||
}
|
||||
this.message_is_follower=false;
|
||||
this.display_buttons();
|
||||
return false;
|
||||
},
|
||||
|
||||
fetch_followers: function (value_) {
|
||||
this.value = value_ || {};
|
||||
this.message_is_follower = (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
|
||||
if(value_)
|
||||
return this.ds_follow.call('read', [this.value, ['name', 'user_ids']]).pipe(this.proxy('display_followers'), this.proxy('display_generic'));
|
||||
return this.ds_follow.call('read', [this.value, ['name', 'user_ids']])
|
||||
.pipe(this.proxy('display_followers'), this.proxy('fetch_generic'))
|
||||
.pipe(this.proxy('display_buttons'))
|
||||
.pipe(this.proxy('fetch_subtypes'));
|
||||
},
|
||||
|
||||
/** Read on res.partner failed: fall back on a generic case
|
||||
- fetch current user partner_id (call because no other smart solution currently) FIXME
|
||||
- then display a generic message about followers */
|
||||
fetch_generic: function (error, event) {
|
||||
var self = this;
|
||||
event.preventDefault();
|
||||
return this.ds_users.call('read', [this.session.uid, ['partner_id']]).pipe(function (results) {
|
||||
var pid = results['partner_id'][0];
|
||||
self.message_is_follower = (_.indexOf(self.get('value'), pid) != -1);
|
||||
}).pipe(self.proxy('display_generic'));
|
||||
},
|
||||
|
||||
/* Display generic info about follower, for people not having access to res_partner */
|
||||
display_generic: function (error, event) {
|
||||
event.preventDefault();
|
||||
display_generic: function () {
|
||||
var self = this;
|
||||
var node_user_list = this.$('ul.oe_mail_followers_display').empty();
|
||||
// format content: Followers (You and 0 other) // Followers (3)
|
||||
var content = this.options.title;
|
||||
if (this.message_is_follower) {
|
||||
content += ' (You and ' + (this.value.length-1) + ' other)';
|
||||
content += ' (You and ' + (this.get('value').length-1) + ' other)';
|
||||
}
|
||||
else {
|
||||
content += ' (' + this.value.length + ')'
|
||||
content += ' (' + this.get('value').length + ')'
|
||||
}
|
||||
this.$('div.oe_mail_recthread_followers h4').html(content);
|
||||
this.display_buttons();
|
||||
return $.when();
|
||||
},
|
||||
|
||||
/** Display the followers, evaluate is_follower directly */
|
||||
/** Display the followers */
|
||||
display_followers: function (records) {
|
||||
var self = this;
|
||||
records = records || [];
|
||||
this.message_is_follower = this.set_is_follower(records);
|
||||
// clean and display title
|
||||
var node_user_list = this.$('ul.oe_mail_followers_display').empty();
|
||||
this.$('div.oe_mail_recthread_followers h4').html(this.options.title + (records.length>=5 ? ' (' + records.length + ')' : '') );
|
||||
for(var i=0; i<records.length&&i<5; i++) {
|
||||
var record=records[i];
|
||||
this.$('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')');
|
||||
// truncate number of displayed followers
|
||||
truncated = records.splice(0, this.options.displayed_nb);
|
||||
_(truncated).each(function (record) {
|
||||
record.avatar_url = mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record.id);
|
||||
$(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(node_user_list);
|
||||
});
|
||||
if (truncated.length < records.length) {
|
||||
$('<li>And ' + (records.length - truncated.length) + ' more.</li>').appendTo(node_user_list);
|
||||
}
|
||||
self.set_is_follower(records);
|
||||
},
|
||||
|
||||
/** Computes whether the current user is in the followers */
|
||||
set_is_follower: function (records) {
|
||||
var user_ids = _.pluck(_.pluck(records, 'user_ids'), 0);
|
||||
return _.indexOf(user_ids, this.session.uid) != -1;
|
||||
},
|
||||
|
||||
display_buttons: function () {
|
||||
|
@ -149,15 +168,25 @@ openerp_mail_followers = function(session, mail) {
|
|||
else {
|
||||
this.$('button.oe_follower').removeClass('oe_following').addClass('oe_notfollow');
|
||||
}
|
||||
|
||||
|
||||
if (this.view.is_action_enabled('edit'))
|
||||
this.$('span.oe_mail_invite_wrapper').hide();
|
||||
else
|
||||
this.$('span.oe_mail_invite_wrapper').show();
|
||||
},
|
||||
|
||||
set_subtypes:function(data){
|
||||
/** Fetch subtypes, only if current user is follower */
|
||||
fetch_subtypes: function () {
|
||||
var subtype_list_ul = this.$('.oe_subtypes').empty();
|
||||
if (! this.message_is_follower) return;
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).pipe(this.proxy('display_subtypes'));
|
||||
},
|
||||
|
||||
/** Display subtypes: {'name': default, followed} */
|
||||
display_subtypes:function (data) {
|
||||
var self = this;
|
||||
var subtype_list_ul = this.$('.oe_subtypes');
|
||||
var records = (data[this.view.datarecord.id] || data[null]).message_subtype_data;
|
||||
|
||||
_(records).each(function (record, record_name) {
|
||||
|
@ -167,27 +196,15 @@ openerp_mail_followers = function(session, mail) {
|
|||
});
|
||||
},
|
||||
|
||||
/** Display subtypes: {'name': default, followed} */
|
||||
display_subtypes: function (visible) {
|
||||
var self = this;
|
||||
var recthread_subtypes = self.$('.oe_recthread_subtypes');
|
||||
subtype_list_ul = self.$('ul.oe_subtypes');
|
||||
|
||||
if(subtype_list_ul.is(":empty")) {
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
this.ds_model.call('get_message_subtypes',[[self.view.datarecord.id], context]).pipe(this.proxy('set_subtypes'));
|
||||
}
|
||||
},
|
||||
|
||||
do_follow: function () {
|
||||
_(this.$('.oe_msg_subtype_check')).each(function(record){
|
||||
$(record).attr('checked','checked');
|
||||
_(this.$('.oe_msg_subtype_check')).each(function (record) {
|
||||
$(record).attr('checked', 'checked');
|
||||
});
|
||||
this.do_update_subscription();
|
||||
},
|
||||
|
||||
do_unfollow: function () {
|
||||
_(this.$('.oe_msg_subtype_check')).each(function(record){
|
||||
_(this.$('.oe_msg_subtype_check')).each(function (record) {
|
||||
$(record).attr('checked',false);
|
||||
});
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
|
@ -198,22 +215,15 @@ openerp_mail_followers = function(session, mail) {
|
|||
var self = this;
|
||||
|
||||
var checklist = new Array();
|
||||
_(this.$('.oe_mail_recthread_actions input[type="checkbox"]')).each(function(record){
|
||||
if($(record).is(':checked')) {
|
||||
checklist.push(parseInt($(record).data('id')))}
|
||||
_(this.$('.oe_mail_recthread_actions input[type="checkbox"]')).each(function (record) {
|
||||
if ($(record).is(':checked')) {
|
||||
checklist.push(parseInt($(record).data('id')));
|
||||
}
|
||||
});
|
||||
|
||||
if(!checklist.length)
|
||||
return this.do_unfollow();
|
||||
else{
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], undefined, context]).pipe(function(value_){
|
||||
self.read_value(value_);
|
||||
self.display_subtypes(true);
|
||||
});
|
||||
}
|
||||
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist:undefined, context])
|
||||
.pipe(this.proxy('read_value'));
|
||||
},
|
||||
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<t t-call="HiddenInputFile">
|
||||
<t t-set="fileupload_id" t-value="widget.fileupload_id"/>
|
||||
<t t-set="fileupload_action">/web/binary/upload_attachment</t>
|
||||
<input type="hidden" name="model" value=""/>
|
||||
<input type="hidden" name="model" value="mail.message"/>
|
||||
<input type="hidden" name="id" value="0"/>
|
||||
<input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
|
||||
</t>
|
||||
|
@ -95,8 +95,8 @@
|
|||
<t t-name="mail.thread.list_recipients">
|
||||
<div class="oe_mail_list_recipients">
|
||||
Post to:
|
||||
<span t-if="!widget.is_private" class="oe_all_follower">All Followers</span>
|
||||
<t t-if="!widget.is_private and widget.partner_ids.length"> and </t>
|
||||
<span t-if="widget.context.default_res_id and widget.context.default_res_id" class="oe_all_follower">All Followers</span>
|
||||
<t t-if="!widget.context.default_res_id and widget.context.default_res_id and widget.partner_ids.length"> and </t>
|
||||
<t t-set="inc" t-value="0"/>
|
||||
<t t-if="widget.partner_ids.length" t-foreach="widget.partner_ids" t-as="partner"><span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/><a t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-raw="partner[1]"/></a></span><t t-set="inc" t-value="inc+1"/>
|
||||
</t>
|
||||
|
@ -186,7 +186,7 @@
|
|||
</div>
|
||||
|
||||
<!-- default layout -->
|
||||
<li t-name="mail.thread.message" t-attf-class="oe_mail oe_mail_thread_msg #{widget.unread?'oe_mail_unread':'oe_mail_read'}">
|
||||
<li t-name="mail.thread.message" t-attf-class="oe_mail oe_mail_thread_msg #{widget.to_read ?'oe_mail_unread':'oe_mail_read'}">
|
||||
<div t-attf-class="oe_mail_msg_#{widget.type} oe_semantic_html_override">
|
||||
<!-- message actions (read/unread, reply, delete...) -->
|
||||
<ul class="oe_header">
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
-->
|
||||
<div t-name="mail.followers" class="oe_mail_recthread_aside oe_semantic_html_override">
|
||||
<div class="oe_mail_recthread_actions">
|
||||
<div class="oe_mouse_subtypes">
|
||||
<div class="oe_mail_subtypes">
|
||||
<button type="button" class="oe_follower oe_notfollow">
|
||||
<span class="oe_follow">Follow</span>
|
||||
<span class="oe_unfollow">Unfollow</span>
|
||||
|
@ -18,11 +18,9 @@
|
|||
<ul class="oe_subtypes"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_grey">
|
||||
<t t-if="widget.options.comment">
|
||||
<h5><t t-raw="widget.options.comment"/></h5>
|
||||
</t>
|
||||
</div>
|
||||
<t t-if="widget.options.comment">
|
||||
<h5 class="oe_grey"><t t-raw="widget.options.comment"/></h5>
|
||||
</t>
|
||||
<div class="oe_mail_recthread_followers">
|
||||
<button type="button" class="oe_invite"><span>Invite</span></button>
|
||||
<t t-if="widget.options.title">
|
||||
|
|
|
@ -135,7 +135,9 @@ class test_mail(TestMailMockups):
|
|||
self.res_users = self.registry('res.users')
|
||||
self.res_partner = self.registry('res.partner')
|
||||
|
||||
self.user_demo = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, 'base', 'user_demo')[1]
|
||||
# Test users
|
||||
self.user_demo_id = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, 'base', 'user_demo')[1]
|
||||
self.user_admin = self.res_users.browse(self.cr, self.uid, self.uid)
|
||||
|
||||
# Mock send_get_mail_body to test its functionality without other addons override
|
||||
self._send_get_mail_body = self.registry('mail.mail').send_get_mail_body
|
||||
|
@ -148,6 +150,7 @@ class test_mail(TestMailMockups):
|
|||
# create a 'pigs' group that will be used through the various tests
|
||||
self.group_pigs_id = self.mail_group.create(self.cr, self.uid,
|
||||
{'name': 'Pigs', 'description': 'Fans of Pigs, unite !'})
|
||||
self.group_pigs = self.mail_group.browse(self.cr, self.uid, self.group_pigs_id)
|
||||
|
||||
def tearDown(self):
|
||||
# Remove mocks
|
||||
|
@ -155,6 +158,7 @@ class test_mail(TestMailMockups):
|
|||
super(test_mail, self).tearDown()
|
||||
|
||||
def test_00_message_process(self):
|
||||
""" Testing incoming emails processing. """
|
||||
cr, uid = self.cr, self.uid
|
||||
# Incoming mail creates a new mail_group "frogs"
|
||||
self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', 'frogs')]), [])
|
||||
|
@ -193,18 +197,15 @@ class test_mail(TestMailMockups):
|
|||
self.assertEqual(new_mail.body, '\n<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>\n',
|
||||
'plaintext mail incorrectly parsed')
|
||||
|
||||
def test_10_many2many_reference_field(self):
|
||||
def test_10_followers_function_field(self):
|
||||
""" Tests designed for the many2many function field 'follower_ids'.
|
||||
We will test to perform writes using the many2many commands 0, 3, 4,
|
||||
5 and 6. """
|
||||
cr, uid = self.cr, self.uid
|
||||
user_admin = self.res_users.browse(cr, uid, uid)
|
||||
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
|
||||
# Create partner Bert Poilu
|
||||
# Data: create partner Bert Poilu
|
||||
partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Poilu'})
|
||||
|
||||
# Create 'disturbing' values in mail.followers: same res_id, other res_model; same res_model, other res_id
|
||||
# Data: create 'disturbing' values in mail.followers: same res_id, other res_model; same res_model, other res_id
|
||||
group_dummy_id = self.mail_group.create(cr, uid,
|
||||
{'name': 'Dummy group'})
|
||||
self.mail_followers.create(cr, uid,
|
||||
|
@ -257,9 +258,7 @@ class test_mail(TestMailMockups):
|
|||
|
||||
def test_11_message_followers_and_subtypes(self):
|
||||
""" Tests designed for the subscriber API as well as message subtypes """
|
||||
cr, uid = self.cr, self.uid
|
||||
user_admin = self.res_users.browse(cr, uid, uid)
|
||||
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
# Data: user Raoul
|
||||
user_raoul_id = self.res_users.create(cr, uid, {'name': 'Raoul Grosbedon', 'login': 'raoul'})
|
||||
user_raoul = self.res_users.browse(cr, uid, user_raoul_id)
|
||||
|
@ -281,7 +280,6 @@ class test_mail(TestMailMockups):
|
|||
group_pigs.refresh()
|
||||
# Test: 2 followers (Admin and Raoul)
|
||||
follower_ids = [follower.id for follower in group_pigs.message_follower_ids]
|
||||
self.assertEqual(len(follower_ids), 2, 'There should be 2 Pigs fans')
|
||||
self.assertEqual(set(follower_ids), set([user_raoul.partner_id.id, user_admin.partner_id.id]), 'Admin and Raoul should be the only 2 Pigs fans')
|
||||
# Test: Raoul follows default subtypes
|
||||
fol_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id), ('partner_id', '=', user_raoul.partner_id.id)])
|
||||
|
@ -307,19 +305,16 @@ class test_mail(TestMailMockups):
|
|||
# CASE2: test mail_thread fields
|
||||
# ----------------------------------------
|
||||
|
||||
group_pigs.refresh()
|
||||
self.assertEqual(set(group_pigs.message_subtype_data.keys()), set(['comment', 'mt_mg_def', 'mt_all_def', 'mt_mg_nodef', 'mt_all_nodef']), 'mail.group available subtypes incorrect')
|
||||
self.assertFalse(group_pigs.message_subtype_data['comment']['followed'], 'Admin should not follow comments in pigs')
|
||||
self.assertTrue(group_pigs.message_subtype_data['mt_mg_nodef']['followed'], 'Admin should follow mt_mg_nodef in pigs')
|
||||
self.assertTrue(group_pigs.message_subtype_data['mt_all_nodef']['followed'], 'Admin should follow mt_all_nodef in pigs')
|
||||
subtype_data = group_pigs._get_subscription_data(None, None)[group_pigs.id]['message_subtype_data']
|
||||
self.assertEqual(set(subtype_data.keys()), set(['comment', 'mt_mg_def', 'mt_all_def', 'mt_mg_nodef', 'mt_all_nodef']), 'mail.group available subtypes incorrect')
|
||||
self.assertFalse(subtype_data['comment']['followed'], 'Admin should not follow comments in pigs')
|
||||
self.assertTrue(subtype_data['mt_mg_nodef']['followed'], 'Admin should follow mt_mg_nodef in pigs')
|
||||
self.assertTrue(subtype_data['mt_all_nodef']['followed'], 'Admin should follow mt_all_nodef in pigs')
|
||||
|
||||
def test_20_message_post(self):
|
||||
""" Tests designed for message_post. """
|
||||
cr, uid = self.cr, self.uid
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
|
||||
user_admin = self.res_users.browse(cr, uid, uid)
|
||||
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
|
||||
|
||||
# 1 - Bert Tartopoils, with email, should receive emails for comments and emails
|
||||
p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
|
||||
# 2 - Carine Poilvache, with email, should never receive emails
|
||||
|
@ -345,11 +340,12 @@ class test_mail(TestMailMockups):
|
|||
# CASE1: post comment, body and subject specified
|
||||
# ----------------------------------------
|
||||
|
||||
# 1. Post a new comment on Pigs
|
||||
self._init_mock_build_email()
|
||||
msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, type='comment', subtype='mt_comment')
|
||||
message = self.mail_message.browse(cr, uid, msg_id)
|
||||
sent_emails = self._build_email_kwargs_list
|
||||
# Test: notifications have been deleted
|
||||
# Test: mail.mail notifications have been deleted
|
||||
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id)]), 'mail.mail notifications should have been auto-deleted!')
|
||||
# Test: mail_message: subject is _subject, body is _body1 (no formatting done)
|
||||
self.assertEqual(message.subject, _subject, 'mail.message subject incorrect')
|
||||
|
@ -377,19 +373,18 @@ class test_mail(TestMailMockups):
|
|||
# CASE2: post an email with attachments, parent_id, partner_ids
|
||||
# ----------------------------------------
|
||||
|
||||
# TESTS: automatic subject, signature in body_html, attachments propagation
|
||||
# 1. Post a new email comment on Pigs
|
||||
self._init_mock_build_email()
|
||||
msg_id2 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body2, type='email', subtype='mt_comment',
|
||||
partner_ids=[(6, 0, [p_d_id])], parent_id=msg_id, attachments=_attachments)
|
||||
message = self.mail_message.browse(cr, uid, msg_id2)
|
||||
sent_emails = self._build_email_kwargs_list
|
||||
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id2)]), 'mail.mail notifications should have been auto-deleted!')
|
||||
|
||||
# Test: mail_message: subject is False, body is _body2 (no formatting done), parent_id is msg_id
|
||||
self.assertEqual(message.subject, False, 'mail.message subject incorrect')
|
||||
self.assertEqual(message.body, html_sanitize(_body2), 'mail.message body incorrect')
|
||||
self.assertEqual(message.parent_id.id, msg_id, 'mail.message parent_id incorrect')
|
||||
# Test: sent_email: email send by server: correct subject, body, body_alternative
|
||||
# Test: sent_email: email send by server: correct automatic subject, body, body_alternative
|
||||
self.assertEqual(len(sent_emails), 2, 'sent_email number of sent emails incorrect')
|
||||
for sent_email in sent_emails:
|
||||
self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect')
|
||||
|
@ -413,13 +408,11 @@ class test_mail(TestMailMockups):
|
|||
self.assertIn((attach.name, attach.datas.decode('base64')), _attachments,
|
||||
'mail.message attachment name / data incorrect')
|
||||
|
||||
def test_21_message_compose_wizard(self):
|
||||
def test_25_message_compose_wizard(self):
|
||||
""" Tests designed for the mail.compose.message wizard. """
|
||||
cr, uid = self.cr, self.uid
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
mail_compose = self.registry('mail.compose.message')
|
||||
self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
|
||||
user_admin = self.res_users.browse(cr, uid, uid)
|
||||
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
|
||||
group_bird_id = self.mail_group.create(cr, uid, {'name': 'Bird', 'description': 'Bird resistance'})
|
||||
group_bird = self.mail_group.browse(cr, uid, group_bird_id)
|
||||
|
||||
|
@ -434,7 +427,6 @@ class test_mail(TestMailMockups):
|
|||
]
|
||||
_attachments_test = [('first.txt', 'My first attachment'), ('second.txt', 'My second attachment')]
|
||||
|
||||
# Create partners
|
||||
# 1 - Bert Tartopoils, with email, should receive emails for comments and emails
|
||||
p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
|
||||
# 2 - Carine Poilvache, with email, should never receive emails
|
||||
|
@ -470,7 +462,6 @@ class test_mail(TestMailMockups):
|
|||
msg_pids = [partner.id for partner in message.partner_ids]
|
||||
test_pids = [p_b_id, p_c_id, p_d_id]
|
||||
notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
|
||||
|
||||
self.assertEqual(len(notif_ids), 3, 'mail.message: too much notifications created')
|
||||
self.assertEqual(set(msg_pids), set(test_pids), 'mail.message partner_ids incorrect')
|
||||
|
||||
|
@ -531,12 +522,13 @@ class test_mail(TestMailMockups):
|
|||
|
||||
def test_40_needaction(self):
|
||||
""" Tests for mail.message needaction. """
|
||||
cr, uid = self.cr, self.uid
|
||||
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
|
||||
group_pigs_demo = self.mail_group.browse(cr, self.user_demo, self.group_pigs_id)
|
||||
user_admin = self.res_users.browse(cr, uid, uid)
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
user_demo = self.res_users.browse(cr, uid, self.user_demo_id)
|
||||
group_pigs_demo = self.mail_group.browse(cr, self.user_demo_id, self.group_pigs_id)
|
||||
na_admin_base = self.mail_message._needaction_count(cr, uid, domain=[])
|
||||
na_demo_base = self.mail_message._needaction_count(cr, user_demo.id, domain=[])
|
||||
|
||||
# Demo values: check unread notification = needaction on mail.message
|
||||
# Test: number of unread notification = needaction on mail.message
|
||||
notif_ids = self.mail_notification.search(cr, uid, [
|
||||
('partner_id', '=', user_admin.partner_id.id),
|
||||
('read', '=', False)
|
||||
|
@ -544,36 +536,32 @@ class test_mail(TestMailMockups):
|
|||
na_count = self.mail_message._needaction_count(cr, uid, domain=[])
|
||||
self.assertEqual(len(notif_ids), na_count, 'unread notifications count does not match needaction count')
|
||||
|
||||
na_count1 = self.mail_message._needaction_count(cr, uid, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
|
||||
# Post 2 message on group_pigs as admin, 3 messages as demo user
|
||||
# Do: post 2 message on group_pigs as admin, 3 messages as demo user
|
||||
for dummy in range(2):
|
||||
group_pigs.message_post(body='My Body', subtype='mt_comment')
|
||||
for dummy in range(3):
|
||||
group_pigs_demo.message_post(body='My Demo Body', subtype='mt_comment')
|
||||
|
||||
# Check there are 4 new needaction on mail.message
|
||||
# Test: admin has 3 new notifications (from demo), and 3 new needaction
|
||||
notif_ids = self.mail_notification.search(cr, uid, [
|
||||
('partner_id', '=', user_admin.partner_id.id),
|
||||
('read', '=', False)
|
||||
])
|
||||
na_count = self.mail_message._needaction_count(cr, uid, domain=[])
|
||||
self.assertEqual(len(notif_ids), na_count, 'unread notifications count does not match needaction count')
|
||||
|
||||
# Check there are 4 needaction on mail.message with particular domain
|
||||
na_count = self.mail_message._needaction_count(cr, uid, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
|
||||
self.assertEqual(len(notif_ids), na_admin_base + 3, 'Admin should have 3 new unread notifications')
|
||||
na_admin = self.mail_message._needaction_count(cr, uid, domain=[])
|
||||
na_admin_group = self.mail_message._needaction_count(cr, uid, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
|
||||
self.assertEqual(na_admin, na_admin_base + 3, 'Admin should have 3 new needaction')
|
||||
self.assertEqual(na_admin_group, 3, 'Admin should have 3 needaction related to Pigs')
|
||||
# Test: demo has 0 new notifications (not a follower, not receiving its own messages), and 0 new needaction
|
||||
notif_ids = self.mail_notification.search(cr, uid, [
|
||||
('partner_id', '=', user_admin.partner_id.id),
|
||||
('read', '=', False),
|
||||
('message_id.model','=','mail.group'),
|
||||
('message_id.res_id','=',self.group_pigs_id)
|
||||
('partner_id', '=', user_demo.partner_id.id),
|
||||
('read', '=', False)
|
||||
])
|
||||
self.assertEqual(len(notif_ids), na_count, 'posted message count does not match needaction count')
|
||||
|
||||
na_count3 = self.mail_message._needaction_count(cr, self.user_demo, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
|
||||
self.assertEqual(na_count3-na_count1, 0, 'demo has 0 message: not a follower and do not follow his own messages')
|
||||
|
||||
na_count2 = self.mail_message._needaction_count(cr, uid, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
|
||||
self.assertEqual(na_count2-na_count1, 3, 'admin has 3 messages: 0 from itself as they are marked as read, 3 from demo')
|
||||
self.assertEqual(len(notif_ids), na_demo_base + 0, 'Demo should have 0 new unread notifications')
|
||||
na_demo = self.mail_message._needaction_count(cr, user_demo.id, domain=[])
|
||||
na_demo_group = self.mail_message._needaction_count(cr, user_demo.id, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
|
||||
self.assertEqual(na_demo, na_demo_base + 0, 'Demo should have 0 new needaction')
|
||||
self.assertEqual(na_demo_group, 0, 'Demo should have 0 needaction related to Pigs')
|
||||
|
||||
def test_50_thread_parent_resolution(self):
|
||||
"""Verify parent/child relationships are correctly established when processing incoming mails"""
|
||||
|
|
|
@ -102,53 +102,69 @@ class test_mail_access_rights(test_mail.TestMailMockups):
|
|||
""" Test mail_message search override about access rights. """
|
||||
self.assertTrue(1 == 1, 'Test not implemented, do not replace by return True')
|
||||
|
||||
# def test_10_mail_flow_access_rights(self):
|
||||
# """ Test a Chatter-looks alike flow. """
|
||||
# cr, uid = self.cr, self.uid
|
||||
# partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
|
||||
# user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
|
||||
def test_10_mail_flow_access_rights(self):
|
||||
""" Test a Chatter-looks alike flow. """
|
||||
cr, uid = self.cr, self.uid
|
||||
mail_compose = self.registry('mail.compose.message')
|
||||
partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
|
||||
user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
|
||||
|
||||
# # Prepare groups: Pigs (employee), Jobs (public)
|
||||
# self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
|
||||
# self.group_jobs_id = self.mail_group.create(cr, uid, {'name': 'Jobs', 'public': 'public'})
|
||||
# Prepare groups: Pigs (employee), Jobs (public)
|
||||
self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
|
||||
self.group_jobs_id = self.mail_group.create(cr, uid, {'name': 'Jobs', 'public': 'public'})
|
||||
|
||||
# # ----------------------------------------
|
||||
# # CASE1: Bert, without groups
|
||||
# # ----------------------------------------
|
||||
# # Do: Bert creates a group, should crash because perm_create only for employees
|
||||
# self.assertRaises(except_orm,
|
||||
# self.mail_group.create,
|
||||
# cr, user_bert_id, {'name': 'Bert\'s Group'})
|
||||
# # Do: Bert reads Jobs basic fields, ok because public = read access on the group
|
||||
# self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['name', 'description'])
|
||||
# # Do: Bert browse Pigs, ok (no direct browse of partners)
|
||||
# self.mail_group.browse(cr, user_bert_id, self.group_jobs_id)
|
||||
# # Do: Bert reads Jobs messages, ok because read access on the group => read access on its messages
|
||||
# jobs_message_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_ids'])['message_ids']
|
||||
# self.mail_message.read(cr, user_bert_id, jobs_message_ids)
|
||||
# # Do: Bert reads Jobs followers, ko because partner are accessible to employees or partner manager
|
||||
# jobs_followers_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_follower_ids'])['message_follower_ids']
|
||||
# self.assertRaises(except_orm,
|
||||
# self.res_partner.read,
|
||||
# cr, user_bert_id, jobs_followers_ids)
|
||||
# # Do: Bert comments Jobs, ko because no write access on the group and not in the followers
|
||||
# self.assertRaises(except_orm,
|
||||
# self.mail_group.message_post,
|
||||
# cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
|
||||
# # Do: add Bert to jobs followers
|
||||
# self.mail_group.message_subscribe(cr, uid, [self.group_jobs_id], [partner_bert_id])
|
||||
# # Do: Bert comments Jobs, ok because he is now in the followers
|
||||
# self.mail_group.message_post(cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
|
||||
# ----------------------------------------
|
||||
# CASE1: Bert, without groups
|
||||
# ----------------------------------------
|
||||
# Do: Bert creates a group, should crash because perm_create only for employees
|
||||
self.assertRaises(except_orm,
|
||||
self.mail_group.create,
|
||||
cr, user_bert_id, {'name': 'Bert\'s Group'})
|
||||
|
||||
# # Do: Bert reads Pigs, should crash because mail.group security=groups only for employee group
|
||||
# self.assertRaises(except_orm,
|
||||
# self.mail_group.read,
|
||||
# cr, user_bert_id, self.group_pigs_id)
|
||||
# Do: Bert reads Jobs basic fields, ok because public = read access on the group
|
||||
self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['name', 'description'])
|
||||
# Do: Bert browse Pigs, ok (no direct browse of partners)
|
||||
self.mail_group.browse(cr, user_bert_id, self.group_jobs_id)
|
||||
# Do: Bert reads Jobs messages, ok because read access on the group => read access on its messages
|
||||
jobs_message_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_ids'])['message_ids']
|
||||
self.mail_message.read(cr, user_bert_id, jobs_message_ids)
|
||||
# Do: Bert reads Jobs followers, ko because partner are accessible to employees or partner manager
|
||||
jobs_followers_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_follower_ids'])['message_follower_ids']
|
||||
self.assertRaises(except_orm,
|
||||
self.res_partner.read,
|
||||
cr, user_bert_id, jobs_followers_ids)
|
||||
# Do: Bert comments Jobs, ko because no write access on the group and not in the followers
|
||||
self.assertRaises(except_orm,
|
||||
self.mail_group.message_post,
|
||||
cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
|
||||
# Do: add Bert to jobs followers
|
||||
self.mail_group.message_subscribe(cr, uid, [self.group_jobs_id], [partner_bert_id])
|
||||
# Do: Bert comments Jobs, ok because he is now in the followers
|
||||
self.mail_group.message_post(cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
|
||||
|
||||
# # ----------------------------------------
|
||||
# # CASE1: Raoul, employee
|
||||
# # ----------------------------------------
|
||||
# # Do: Bert read Pigs, ok because public
|
||||
# self.mail_group.read(cr, user_raoul_id, self.group_pigs_id)
|
||||
# # Do: Bert read Jobs, ok because group_public_id = employee
|
||||
# self.mail_group.read(cr, user_raoul_id, self.group_jobs_id)
|
||||
# Do: Bert reads Pigs, should crash because mail.group security=groups only for employee group
|
||||
self.assertRaises(except_orm,
|
||||
self.mail_group.read,
|
||||
cr, user_bert_id, self.group_pigs_id)
|
||||
|
||||
# Do: Bert create a mail.compose.message record, because he uses the wizard
|
||||
compose_id = mail_compose.create(cr, user_bert_id,
|
||||
{'subject': 'Subject', 'body_text': 'Body text', 'partner_ids': []},
|
||||
# {'subject': 'Subject', 'body_text': 'Body text', 'partner_ids': [(4, p_c_id), (4, p_d_id)]},
|
||||
{'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_jobs_id})
|
||||
mail_compose.send_mail(cr, user_bert_id, [compose_id])
|
||||
|
||||
self.user_demo_id = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, 'base', 'user_demo')[1]
|
||||
compose_id = mail_compose.create(cr, self.user_demo_id,
|
||||
{'subject': 'Subject', 'body_text': 'Body text', 'partner_ids': []},
|
||||
# {'subject': 'Subject', 'body_text': 'Body text', 'partner_ids': [(4, p_c_id), (4, p_d_id)]},
|
||||
{'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_jobs_id})
|
||||
mail_compose.send_mail(cr, self.user_demo_id, [compose_id])
|
||||
|
||||
# ----------------------------------------
|
||||
# CASE2: Raoul, employee
|
||||
# ----------------------------------------
|
||||
# Do: Bert read Pigs, ok because public
|
||||
self.mail_group.read(cr, user_raoul_id, self.group_pigs_id)
|
||||
# Do: Bert read Jobs, ok because group_public_id = employee
|
||||
self.mail_group.read(cr, user_raoul_id, self.group_jobs_id)
|
||||
|
|
|
@ -41,6 +41,8 @@ def get_sys_logs(cr, uid):
|
|||
nbr_share_users = pool.get("res.users").search(cr, uid, [("share", "=", True)], count=True)
|
||||
nbr_active_share_users = pool.get("res.users").search(cr, uid, [("share", "=", True), ("date", ">=", limit_date_str)], count=True)
|
||||
user = pool.get("res.users").browse(cr, uid, uid)
|
||||
|
||||
web_base_url = safe_eval(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', 'False'))
|
||||
msg = {
|
||||
"dbuuid": dbuuid,
|
||||
"nbr_users": nbr_users,
|
||||
|
@ -51,6 +53,7 @@ def get_sys_logs(cr, uid):
|
|||
"db_create_date": db_create_date,
|
||||
"version": release.version,
|
||||
"language": user.lang,
|
||||
"web_base_url": web_base_url,
|
||||
}
|
||||
msg.update(pool.get("res.company").read(cr,uid,[1],["name","email","phone"])[0])
|
||||
|
||||
|
|
|
@ -81,11 +81,7 @@ class mail_compose_message(osv.TransientModel):
|
|||
elif composition_mode == 'comment' and model and res_id:
|
||||
vals = self.get_record_data(cr, uid, model, res_id, context=context)
|
||||
elif composition_mode == 'mass_mail' and model and active_ids:
|
||||
if context.get('default_template_id'):
|
||||
vals = self.pool.get('email.template').generate_email(cr, uid, context.get('default_template_id'), res_id, context=context)
|
||||
vals.update({'content_subtype': 'html'})
|
||||
else:
|
||||
vals = {'model': model, 'res_id': res_id, 'content_subtype': 'html'}
|
||||
vals = {'model': model, 'res_id': res_id, 'content_subtype': 'html'}
|
||||
else:
|
||||
vals = {'model': model, 'res_id': res_id}
|
||||
if composition_mode:
|
||||
|
@ -229,22 +225,13 @@ class mail_compose_message(osv.TransientModel):
|
|||
mass_mail_mode = wizard.composition_mode == 'mass_mail'
|
||||
active_model_pool = self.pool.get(wizard.model if wizard.model else 'mail.thread')
|
||||
|
||||
if wizard.content_subtype == 'html':
|
||||
if not wizard.body:
|
||||
return False
|
||||
body = wizard.body
|
||||
else: # wizard.content_subtype == 'plain':
|
||||
if not wizard.body_text:
|
||||
return False
|
||||
body = '<pre>%s</pre>' % tools.ustr(wizard.body_text or '')
|
||||
|
||||
# wizard works in batch mode: [res_id] or active_ids
|
||||
res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id]
|
||||
for res_id in res_ids:
|
||||
# default values, according to the wizard options
|
||||
post_values = {
|
||||
'subject': wizard.subject if wizard.content_subtype == 'html' else False,
|
||||
'body': body,
|
||||
'body': wizard.body if wizard.content_subtype == 'html' else '<pre>%s</pre>' % tools.ustr(wizard.body_text),
|
||||
'parent_id': wizard.parent_id and wizard.parent_id.id,
|
||||
'partner_ids': [(4, partner.id) for partner in wizard.partner_ids],
|
||||
'attachments': [(attach.datas_fname or attach.name, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids],
|
||||
|
@ -258,13 +245,13 @@ class mail_compose_message(osv.TransientModel):
|
|||
post_values['attachments'] += new_attachments
|
||||
post_values.update(email_dict)
|
||||
# post the message
|
||||
id=active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype='mt_comment', context=context, **post_values)
|
||||
active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype='mt_comment', context=context, **post_values)
|
||||
|
||||
# post process: update attachments, because id is not necessarily known when adding attachments in Chatter
|
||||
# self.pool.get('ir.attachment').write(cr, uid, [attach.id for attach in wizard.attachment_ids], {
|
||||
# 'res_id': wizard.id, 'res_model': wizard.model or False}, context=context)
|
||||
|
||||
return {'type': 'ir.actions.act_window_close', 'res_model':'mail.compose.message', 'id': id}
|
||||
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def render_message(self, cr, uid, wizard, res_id, context=None):
|
||||
""" Generate an email from the template for given (wizard.model, res_id)
|
||||
|
|
|
@ -6,17 +6,19 @@
|
|||
<field name="model">mail.compose.message</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Compose Email" version="7.0">
|
||||
<field name="composition_mode" nolabel="1" invisible="1"/>
|
||||
<field name="model" nolabel="1" invisible="1"/>
|
||||
<field name="res_id" nolabel="1" invisible="1"/>
|
||||
<field name="parent_id" nolabel="1" invisible="1"/>
|
||||
<field name="content_subtype" nolabel="1" invisible="1"/>
|
||||
<!-- truly invisible fields for control and options -->
|
||||
<field name="composition_mode" nolabel="1" invisible="0"/>
|
||||
<field name="model" nolabel="1" invisible="0"/>
|
||||
<field name="res_id" nolabel="1" invisible="0"/>
|
||||
<field name="parent_id" nolabel="1" invisible="0"/>
|
||||
<field name="content_subtype" nolabel="1" invisible="0"/>
|
||||
<!-- visible wizard -->
|
||||
<group>
|
||||
<field name="subject" placeholder="Subject..."/>
|
||||
<field name="subject" placeholder="Subject..."
|
||||
attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>/>
|
||||
<field name="partner_ids" widget="many2many_tags" placeholder="Add contacts to notify..."
|
||||
context="{'force_email':True}"
|
||||
on_change="onchange_partner_ids(partner_ids)"/>
|
||||
<field name="is_private" help="If this message is not private, this message will send to all your followers or all followers of the parented message."/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Body">
|
||||
|
@ -30,68 +32,11 @@
|
|||
<button string="Send" name="send_mail" type="object" class="oe_highlight" />
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
<!--div class="oe_right">
|
||||
<button string="" name="toggle_content_subtype" type="object" icon="/mail/static/src/img/formatting.png"
|
||||
help="Toggle advanced formatting mode"/>
|
||||
</div-->
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="email_compose_message_wizard_form_chatter">
|
||||
<field name="name">mail.compose.message.form.chatter</field>
|
||||
<field name="model">mail.compose.message</field>
|
||||
<field name="priority">18</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Compose Email" version="7.0" >
|
||||
<group>
|
||||
<!-- truly invisible fields for control and options -->
|
||||
<field name="composition_mode" colspan="2" nolabel="1" invisible="1"/>
|
||||
<field name="model" colspan="2" nolabel="1" invisible="1"/>
|
||||
<field name="res_id" colspan="2" nolabel="1" invisible="1"/>
|
||||
<field name="parent_id" colspan="2" nolabel="1" invisible="1"/>
|
||||
<field name="content_subtype" colspan="2" nolabel="1" invisible="1"/>
|
||||
<!-- visible wizard -->
|
||||
<field name="subject" colspan="2" nolabel="1" placeholder="Subject..."
|
||||
class="oe_mail_compose_message_subject"
|
||||
attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
|
||||
<field name="body_text" colspan="2" nolabel="1" placeholder="What are you working on ?"
|
||||
class="oe_mail_compose_message_body"
|
||||
attrs="{'invisible':[('content_subtype', '=', 'html')]}"/>
|
||||
<field name="body" colspan="2" nolabel="1" placeholder="What are you working on ?"
|
||||
class="oe_mail_compose_message_body_html"
|
||||
attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
|
||||
<field name="partner_ids" colspan="2" nolabel="1" widget="many2many_tags" placeholder="Add contacts to notify..."
|
||||
context="{'force_email':True}"
|
||||
on_change="onchange_partner_ids(partner_ids)"
|
||||
class="oe_mail_compose_message_partner_ids"/>
|
||||
<!--field name="is_private" help="If this message is not private, this message will send to all your followers or all followers of the parented message."/-->
|
||||
<field name="attachment_ids" colspan="2" nolabel="1" widget="many2many_tags"
|
||||
placeholder="Add attachments..." invisible="1"
|
||||
class="oe_mail_compose_message_attachment_ids"/>
|
||||
<!-- void div to display attachments, Chatter-controlled -->
|
||||
<div colspan="2" class="oe_mail_compose_message_attachments"/>
|
||||
<!-- buttons, with as few Chatter logic as possible -->
|
||||
<div>
|
||||
<button name="send_mail" string="Post message" type="object"
|
||||
class="oe_mail_compose_message_button_send"/>
|
||||
</div>
|
||||
<div class='oe_mail_compose_message_icons'>
|
||||
<button icon="/mail/static/src/img/attachment.png"
|
||||
class="oe_mail_compose_message_attachment" string=""
|
||||
name="dummy"
|
||||
help="Add an attachment"/>
|
||||
<button icon="/mail/static/src/img/formatting.png"
|
||||
class="oe_mail_compose_message_formatting" string=""
|
||||
type="object" name="toggle_content_subtype"
|
||||
help="Toggle advanced formatting mode"/>
|
||||
</div>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_email_compose_message_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Compose Email</field>
|
||||
<field name="res_model">mail.compose.message</field>
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: mrp_repair
|
||||
#: view:mrp.repair:0
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:39+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: point_of_sale
|
||||
#: field:report.transaction.pos,product_nb:0
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:39+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: point_of_sale
|
||||
#: field:report.transaction.pos,product_nb:0
|
||||
|
|
|
@ -897,7 +897,7 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
margin-right: 18px
|
||||
margin-right: 18px;
|
||||
font-family: "Inconsolata";
|
||||
color: #6c6c6c;
|
||||
text-shadow: 0px 3px 3px rgba(0,0,0, 0.2);
|
||||
|
|
|
@ -44,7 +44,7 @@ a.cta-a strong {
|
|||
background-color:#FFF;
|
||||
}
|
||||
.process_canvas svg{
|
||||
height:500px;!important;
|
||||
height:500px;
|
||||
padding:15px;
|
||||
}
|
||||
.oe_process {
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:39+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: project
|
||||
#: view:report.project.task.user:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: project_long_term
|
||||
#: model:ir.actions.act_window,name:project_long_term.act_project_phases
|
||||
|
|
|
@ -294,8 +294,8 @@
|
|||
<field name="model">sale.order</field>
|
||||
<field name="res_id" ref="sale_order_2"/>
|
||||
<field name="body">Hi,
|
||||
I have a confusion for pricing of Services, I have heard there is a discount above 25 hours.
|
||||
Can you clarify please?</field>
|
||||
I have a question regarding services pricing: I heard of a possible discount for quantities exceeding 25 hours.
|
||||
Could you confirm, please?</field>
|
||||
<field name="type">comment</field>
|
||||
<field name="author_id" ref="base.partner_demo"/>
|
||||
</record>
|
||||
|
@ -305,10 +305,9 @@ Can you clarify please?</field>
|
|||
<field name="res_id" ref="sale_order_2"/>
|
||||
<field name="parent_id" ref="message_sale_1"/>
|
||||
<field name="body">Hello,
|
||||
Sorry but that scheme is not available for now,
|
||||
We would like to know if you confirm the quotation with pricing we sent to you.
|
||||
Thanks,
|
||||
Sales Department</field>
|
||||
Unfortunately that was a temporary discount that is not available anymore.
|
||||
Do you still plan to confirm the order based on the quoted prices?
|
||||
Thanks!</field>
|
||||
<field name="type">comment</field>
|
||||
<field name="author_id" ref="base.partner_root"/>
|
||||
</record>
|
||||
|
@ -317,7 +316,7 @@ Sales Department</field>
|
|||
<field name="model">sale.order</field>
|
||||
<field name="res_id" ref="sale_order_2"/>
|
||||
<field name="parent_id" ref="message_sale_2"/>
|
||||
<field name="body">Ok, fine, we will intimate you after discussing with our team.</field>
|
||||
<field name="body">Alright, thanks for the clarification. I will confirm the order as soon as I get my manager's approval.</field>
|
||||
<field name="type">comment</field>
|
||||
<field name="author_id" ref="base.partner_demo"/>
|
||||
</record>
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:39+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: stock
|
||||
#: field:product.product,track_outgoing:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-17 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16152)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-18 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 16160)\n"
|
||||
|
||||
#. module: survey
|
||||
#: view:survey.print:0
|
||||
|
|
Loading…
Reference in New Issue