[MRG] From last trunk
bzr revid: joel.grandguillaume@camptocamp.com-20121029152959-ugtxjoe8rc7mlhcp
This commit is contained in:
commit
85b2285e49
|
@ -39,7 +39,6 @@ class account_analytic_line(osv.osv):
|
|||
}
|
||||
|
||||
_defaults = {
|
||||
'date': fields.date.context_today,
|
||||
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context=c),
|
||||
}
|
||||
_order = 'date desc'
|
||||
|
|
|
@ -865,8 +865,11 @@ class account_invoice(osv.osv):
|
|||
self.check_tax_lines(cr, uid, inv, compute_taxes, ait_obj)
|
||||
|
||||
# I disabled the check_total feature
|
||||
#if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0):
|
||||
# raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.'))
|
||||
group_check_total_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'group_supplier_inv_check_total')[1]
|
||||
group_check_total = self.pool.get('res.groups').browse(cr, uid, group_check_total_id, context=context)
|
||||
if group_check_total and uid in [x.id for x in group_check_total.users]:
|
||||
if (inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0)):
|
||||
raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe encoded total does not match the computed total.'))
|
||||
|
||||
if inv.payment_term:
|
||||
total_fixed = total_percent = 0
|
||||
|
|
|
@ -186,6 +186,7 @@
|
|||
<field name="journal_id" groups="account.group_account_user"
|
||||
on_change="onchange_journal_id(journal_id, context)" widget="selection"/>
|
||||
<field name="currency_id" groups="base.group_multi_currency"/>
|
||||
<field name="check_total" groups="account.group_supplier_inv_check_total"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
|
@ -279,8 +280,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -436,8 +437,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -10,7 +10,7 @@
|
|||
<group col="4">
|
||||
<field name="date1"/>
|
||||
<field name="date2"/>
|
||||
<field name="analytic_account_journal_id" widget="many2many_tags" class="oe_inline" required="1"/>
|
||||
<field name="analytic_account_journal_id" widget="many2many_tags" class="oe_inline" required="1" colspan="4"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="check_report" string="Print" type="object" class="oe_highlight"/>
|
||||
|
|
|
@ -120,6 +120,8 @@ class account_config_settings(osv.osv_memory):
|
|||
'group_analytic_accounting': fields.boolean('Analytic accounting',
|
||||
implied_group='analytic.group_analytic_accounting',
|
||||
help="Allows you to use the analytic accounting."),
|
||||
'group_check_supplier_invoice_total': fields.boolean('Check the total of supplier invoices',
|
||||
implied_group="account.group_supplier_inv_check_total"),
|
||||
}
|
||||
|
||||
def _default_company(self, cr, uid, context=None):
|
||||
|
|
|
@ -220,6 +220,10 @@
|
|||
<field name="module_account_check_writing" class="oe_inline"/>
|
||||
<label for="module_account_check_writing"/>
|
||||
</div>
|
||||
<div>
|
||||
<field name="group_check_supplier_invoice_total" class="oe_inline"/>
|
||||
<label for="group_check_supplier_invoice_total"/>
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
<separator string="Bank & Cash"/>
|
||||
|
|
|
@ -26,6 +26,11 @@
|
|||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_supplier_inv_check_total" model="res.groups">
|
||||
<field name="name">Check Total on supplier invoices</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="account_move_comp_rule" model="ir.rule">
|
||||
<field name="name">Account Entry</field>
|
||||
<field name="model_id" ref="model_account_move"/>
|
||||
|
|
|
@ -74,36 +74,18 @@ class account_analytic_account(osv.osv):
|
|||
for id in ids:
|
||||
res[id][f] = 0.0
|
||||
res2 = {}
|
||||
if parent_ids:
|
||||
# Amount uninvoiced hours to invoice at sale price
|
||||
# Warning
|
||||
# This computation doesn't take care of pricelist !
|
||||
# Just consider list_price
|
||||
cr.execute("""SELECT account_analytic_account.id, \
|
||||
COALESCE(SUM (product_template.list_price * \
|
||||
account_analytic_line.unit_amount * \
|
||||
((100-hr_timesheet_invoice_factor.factor)/100)), 0.0) \
|
||||
AS ca_to_invoice \
|
||||
FROM product_template \
|
||||
JOIN product_product \
|
||||
ON product_template.id = product_product.product_tmpl_id \
|
||||
JOIN account_analytic_line \
|
||||
ON account_analytic_line.product_id = product_product.id \
|
||||
JOIN account_analytic_journal \
|
||||
ON account_analytic_line.journal_id = account_analytic_journal.id \
|
||||
JOIN account_analytic_account \
|
||||
ON account_analytic_account.id = account_analytic_line.account_id \
|
||||
JOIN hr_timesheet_invoice_factor \
|
||||
ON hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \
|
||||
WHERE account_analytic_account.id IN %s \
|
||||
AND account_analytic_line.invoice_id IS NULL \
|
||||
AND account_analytic_line.to_invoice IS NOT NULL \
|
||||
AND account_analytic_journal.type = 'general' \
|
||||
GROUP BY account_analytic_account.id;""", (parent_ids,))
|
||||
for account_id, sum in cr.fetchall():
|
||||
if account_id not in res:
|
||||
res[account_id] = {}
|
||||
res[account_id][f] = round(sum, dp)
|
||||
for account in accounts:
|
||||
cr.execute("SELECT product_id, user_id, to_invoice, sum(unit_amount), product_uom_id, name " \
|
||||
"FROM account_analytic_line as line " \
|
||||
"WHERE account_id = %s " \
|
||||
"AND invoice_id is NULL AND to_invoice IS NOT NULL " \
|
||||
"GROUP BY product_id, user_id, to_invoice, product_uom_id, name", (account.id,))
|
||||
|
||||
res[account.id][f] = 0.0
|
||||
for product_id, user_id, factor_id, qty, uom, line_name in cr.fetchall():
|
||||
price = self.pool.get('account.analytic.line')._get_invoice_price(cr, uid, account, product_id, user_id, qty, context)
|
||||
factor = self.pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context=context)
|
||||
res[account.id][f] += price * qty * (100-factor.factor or 0.0) / 100.0
|
||||
|
||||
# sum both result on account_id
|
||||
for id in ids:
|
||||
|
|
|
@ -30,18 +30,18 @@
|
|||
<separator name="toinvoice" string="Invoicing"/>
|
||||
<table class="oe_form_analytic_account">
|
||||
<tr>
|
||||
<th class="oe_grey" width="160px"></th>
|
||||
<th class="oe_grey" width="25px"></th>
|
||||
<th class="oe_grey" width="100px"><label string="Expected"/></th>
|
||||
<th class="oe_grey" width="100px"><label string="Invoiced"/></th>
|
||||
<th class="oe_grey" width="100px"><label string="Remaining"/></th>
|
||||
<th class="oe_grey" width="100px"><label string="To Invoice"/></th>
|
||||
<th class="oe_timesheet_grey" width="160px"></th>
|
||||
<th class="oe_timesheet_grey" width="25px"></th>
|
||||
<th class="oe_timesheet_grey" width="100px"><label string="Expected"/></th>
|
||||
<th class="oe_timesheet_grey" width="100px"><label string="Invoiced"/></th>
|
||||
<th class="oe_timesheet_grey" width="100px"><label string="Remaining"/></th>
|
||||
<th class="oe_timesheet_grey" width="100px"><label string="To Invoice"/></th>
|
||||
<th width="30px"></th>
|
||||
<th></th>
|
||||
</tr><tr>
|
||||
<td class="oe_grey">
|
||||
<td class="oe_timesheet_grey">
|
||||
<label for="fix_price_invoices"/>
|
||||
</td><td class="oe_grey">
|
||||
</td><td class="oe_timesheet_grey">
|
||||
<field name="fix_price_invoices" class="oe_inline"/>
|
||||
</td><td>
|
||||
<field class="oe_inline" name="amount_max" attrs="{'invisible': [('fix_price_invoices','=',False)]}"/>
|
||||
|
@ -51,23 +51,28 @@
|
|||
<field class="oe_inline" name="remaining_ca" attrs="{'invisible': [('fix_price_invoices','=',False)]}"/>
|
||||
</td><td>
|
||||
<field class="oe_inline" name="fix_price_to_invoice" attrs="{'invisible': [('fix_price_invoices','=',False)]}"/>
|
||||
</td><td>
|
||||
<button name="open_sale_order_lines"
|
||||
class="oe_link oe_e"
|
||||
string="/" type="object"
|
||||
context="{'default_partner_id': [partner_id],'default_project_id': active_id,'search_default_uninvoiced': 1,'search_default_project_id': active_id,'search_default_partner_id': [partner_id]}"
|
||||
attrs="{'invisible': [('fix_price_to_invoice','=',0)]}"/>
|
||||
</td><td>
|
||||
</td><td attrs="{'invisible': [('fix_price_invoices','=',False)]}" class="oe_timesheet_action">
|
||||
<span attrs="{'invisible': [('fix_price_to_invoice','=',0.0)]}" class="oe_grey">
|
||||
<button name="open_sale_order_lines"
|
||||
class="oe_link"
|
||||
string="⇒ Invoice" type="object"
|
||||
context="{'default_partner_id': [partner_id],'default_project_id': active_id,'search_default_uninvoiced': 1,'search_default_project_id': active_id,'search_default_partner_id': [partner_id]}"/>
|
||||
or view
|
||||
</span>
|
||||
|
||||
<span attrs="{'invisible': [('fix_price_to_invoice','<>',0.0)]}" class="oe_grey">
|
||||
No order to invoice, create
|
||||
</span>
|
||||
<button name="%(action_sales_order)d" string="Sale Orders"
|
||||
type="action"
|
||||
class="oe_link"
|
||||
context="{'default_partner_id': [partner_id], 'search_default_partner_id': [partner_id],'search_default_project_id': [active_id],'default_project_id': [active_id]}"
|
||||
attrs="{'invisible': [('fix_price_invoices','=',False)]}"/>
|
||||
/>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td class="oe_grey">
|
||||
<td class="oe_timesheet_grey">
|
||||
<label for="invoice_on_timesheets"/>
|
||||
</td><td class="oe_grey">
|
||||
</td><td class="oe_timesheet_grey">
|
||||
<field name="invoice_on_timesheets"/>
|
||||
</td><td>
|
||||
<field class="oe_inline" name="hours_qtt_est" attrs="{'invisible': [('invoice_on_timesheets','=',False)]}"/>
|
||||
|
@ -77,44 +82,55 @@
|
|||
<field class="oe_inline" name="remaining_hours_to_invoice" attrs="{'invisible': [('invoice_on_timesheets','=',False)]}"/>
|
||||
</td><td>
|
||||
<field class="oe_inline" name="ca_to_invoice" attrs="{'invisible': [('invoice_on_timesheets','=',False)]}"/>
|
||||
</td><td>
|
||||
<button name="%(hr_timesheet_invoice.act_acc_analytic_acc_2_report_acc_analytic_line_to_invoice)d"
|
||||
type="action"
|
||||
class="oe_link oe_e"
|
||||
string="/"
|
||||
attrs="{'invisible': [('ca_to_invoice','=',0.0)]}"/>
|
||||
</td><td class="oe_timesheet_action" attrs="{'invisible': [('invoice_on_timesheets','=',False)]}">
|
||||
<span attrs="{'invisible': [('ca_to_invoice','=',0.0)]}" class="oe_grey">
|
||||
<button name="%(hr_timesheet_invoice.action_hr_timesheet_invoice_create_final)d"
|
||||
type="action"
|
||||
class="oe_link"
|
||||
string="⇒ Invoice"/>
|
||||
or view
|
||||
</span>
|
||||
<span attrs="{'invisible': [('ca_to_invoice','<>',0.0)]}" class="oe_grey">
|
||||
Nothing to invoice, create
|
||||
</span>
|
||||
|
||||
</td><td>
|
||||
<button name="%(hr_timesheet.act_hr_timesheet_line_evry1_all_form)d"
|
||||
string="Timesheets" type="action"
|
||||
class="oe_link"
|
||||
attrs="{'invisible': [('invoice_on_timesheets','=',False)]}"
|
||||
context="{'default_account_id': active_id,'search_default_account_id': active_id}"/>
|
||||
</td>
|
||||
</tr><tr name='total'>
|
||||
<th class="oe_grey">
|
||||
<th class="oe_timesheet_grey">
|
||||
<label string="Total"/>
|
||||
</th><td class="oe_grey">
|
||||
</td><td class="oe_grey">
|
||||
</th><td class="oe_timesheet_grey">
|
||||
</td><td class="oe_timesheet_grey">
|
||||
<field name="est_total" class="oe_inline" attrs="{'invisible': [('invoice_on_timesheets','=',False)]}"/>
|
||||
</td><td class="oe_grey">
|
||||
</td><td class="oe_timesheet_grey">
|
||||
<field name="invoiced_total" class="oe_inline"/>
|
||||
</td><td class="oe_grey">
|
||||
</td><td class="oe_timesheet_grey">
|
||||
<field name="remaining_total" class="oe_inline"/>
|
||||
</td><td class="oe_grey">
|
||||
</td><td class="oe_timesheet_grey">
|
||||
<field name="toinvoice_total" class="oe_inline"/>
|
||||
</td><td>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<group name='invoice_on_timesheets' attrs="{'invisible': [('invoice_on_timesheets','=',False)]}">
|
||||
<field name="pricelist_id"
|
||||
class="oe_inline"
|
||||
attrs="{'required': [('invoice_on_timesheets', '=', True)]}"/>
|
||||
<field name="to_invoice"
|
||||
class="oe_inline"
|
||||
widget="selection"
|
||||
attrs="{'required': [('invoice_on_timesheets', '=', True)]}"/>
|
||||
<p class="oe_grey oe_edit_only" colspan="2">
|
||||
When invoicing on timesheet, OpenERP uses the
|
||||
pricelist of the contract which uses the price
|
||||
defined on the product related to each employee to
|
||||
define the customer invoice price rate.
|
||||
</p>
|
||||
<group>
|
||||
<field name="pricelist_id"
|
||||
class="oe_inline"
|
||||
attrs="{'required': [('invoice_on_timesheets', '=', True)]}"/>
|
||||
<field name="to_invoice"
|
||||
class="oe_inline"
|
||||
widget="selection"
|
||||
attrs="{'required': [('invoice_on_timesheets', '=', True)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
|
|
|
@ -9,7 +9,11 @@
|
|||
float: right;
|
||||
width: auto !important;
|
||||
}
|
||||
.openerp .oe_form table.oe_form_analytic_account .oe_grey {
|
||||
.openerp .oe_form table.oe_form_analytic_account tr td.oe_timesheet_action {
|
||||
width: 250px;
|
||||
padding-left: 7px;
|
||||
}
|
||||
.openerp .oe_form table.oe_form_analytic_account .oe_timesheet_grey {
|
||||
background-color: #eeeeee;
|
||||
color: #404040;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,10 @@
|
|||
.oe_form_field_float
|
||||
float: right
|
||||
width: auto !important
|
||||
.oe_grey
|
||||
td.oe_timesheet_action
|
||||
width: 250px
|
||||
padding-left: 7px
|
||||
.oe_timesheet_grey
|
||||
background-color: #eeeeee
|
||||
color: #404040
|
||||
|
||||
|
|
|
@ -7,14 +7,14 @@ msgstr ""
|
|||
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
|
||||
"PO-Revision-Date: 2011-04-15 19:38+0000\n"
|
||||
"PO-Revision-Date: 2012-10-25 12:32+0000\n"
|
||||
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: account_analytic_plans
|
||||
#: view:analytic.plan.create.model:0
|
||||
|
@ -136,7 +136,7 @@ msgstr "Определить аналитический план счетов"
|
|||
#. module: account_analytic_plans
|
||||
#: constraint:account.invoice:0
|
||||
msgid "Invalid BBA Structured Communication !"
|
||||
msgstr ""
|
||||
msgstr "Неверна структурная связь BBA!"
|
||||
|
||||
#. module: account_analytic_plans
|
||||
#: constraint:account.bank.statement:0
|
||||
|
@ -149,6 +149,8 @@ msgid ""
|
|||
"The date of your Journal Entry is not in the defined period! You should "
|
||||
"change the date or remove this constraint from the journal."
|
||||
msgstr ""
|
||||
"Дата проводки в журнале не в определённом периоде! Вы должны сменить дату "
|
||||
"или удалить это ограничение из журнала."
|
||||
|
||||
#. module: account_analytic_plans
|
||||
#: sql_constraint:account.journal:0
|
||||
|
@ -251,6 +253,8 @@ msgid ""
|
|||
"currency. You should remove the secondary currency on the account or select "
|
||||
"a multi-currency view on the journal."
|
||||
msgstr ""
|
||||
"Выбранный счёт проводки в журнале нуждается во вторичной валюте. Вы должны "
|
||||
"удалить вторичную валюту по счёту или выбрать мульти-валютный вид по журналу."
|
||||
|
||||
#. module: account_analytic_plans
|
||||
#: report:account.analytic.account.crossovered.analytic:0
|
||||
|
@ -429,7 +433,7 @@ msgstr "Счет 4 уровня"
|
|||
#. module: account_analytic_plans
|
||||
#: constraint:account.move.line:0
|
||||
msgid "Company must be the same for its related account and period."
|
||||
msgstr ""
|
||||
msgstr "Для счета и периода должна быть одна компания."
|
||||
|
||||
#. module: account_analytic_plans
|
||||
#: view:account.analytic.plan.instance.line:0
|
||||
|
@ -515,11 +519,14 @@ msgid ""
|
|||
"analytic accounts for each plan set. Then, you must attach a plan set to "
|
||||
"your account journals."
|
||||
msgstr ""
|
||||
"Для настройки нескольких планов счетов аналитики, вы должны определить "
|
||||
"корневые счета аналитики для каждого набора плана. Затем вы можете "
|
||||
"прикрепить набор плана к вашим учётным журналам."
|
||||
|
||||
#. module: account_analytic_plans
|
||||
#: constraint:account.move.line:0
|
||||
msgid "You can not create journal items on closed account."
|
||||
msgstr ""
|
||||
msgstr "Нельзя создать элемент журнала по закрытому счету ."
|
||||
|
||||
#. module: account_analytic_plans
|
||||
#: report:account.analytic.account.crossovered.analytic:0
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
# Polish translation for openobject-addons
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openobject-addons package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
|
||||
"PO-Revision-Date: 2012-10-25 16:56+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: account_check_writing
|
||||
#: selection:res.company,check_layout:0
|
||||
msgid "Check on Top"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: model:ir.actions.act_window,help:account_check_writing.action_write_check
|
||||
msgid ""
|
||||
"The check payment form allows you to track the payment you do to your "
|
||||
"suppliers specially by check. When you select a supplier, the payment method "
|
||||
"and an amount for the payment, OpenERP will propose to reconcile your "
|
||||
"payment with the open supplier invoices or bills.You can print the check"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: view:account.voucher:0
|
||||
#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_bottom
|
||||
#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_middle
|
||||
#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_top
|
||||
msgid "Print Check"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: selection:res.company,check_layout:0
|
||||
msgid "Check in middle"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: help:res.company,check_layout:0
|
||||
msgid ""
|
||||
"Check on top is compatible with Quicken, QuickBooks and Microsoft Money. "
|
||||
"Check in middle is compatible with Peachtree, ACCPAC and DacEasy. Check on "
|
||||
"bottom is compatible with Peachtree, ACCPAC and DacEasy only"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: selection:res.company,check_layout:0
|
||||
msgid "Check on bottom"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: constraint:res.company:0
|
||||
msgid "Error! You can not create recursive companies."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: help:account.journal,allow_check_writing:0
|
||||
msgid "Check this if the journal is to be used for writing checks."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: field:account.journal,allow_check_writing:0
|
||||
msgid "Allow Check writing"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: report:account.print.check.bottom:0
|
||||
#: report:account.print.check.middle:0
|
||||
#: report:account.print.check.top:0
|
||||
msgid "Description"
|
||||
msgstr "Opis"
|
||||
|
||||
#. module: account_check_writing
|
||||
#: model:ir.model,name:account_check_writing.model_account_journal
|
||||
msgid "Journal"
|
||||
msgstr "Dziennik"
|
||||
|
||||
#. module: account_check_writing
|
||||
#: model:ir.actions.act_window,name:account_check_writing.action_write_check
|
||||
#: model:ir.ui.menu,name:account_check_writing.menu_action_write_check
|
||||
msgid "Write Checks"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: report:account.print.check.bottom:0
|
||||
#: report:account.print.check.middle:0
|
||||
#: report:account.print.check.top:0
|
||||
msgid "Discount"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: report:account.print.check.bottom:0
|
||||
#: report:account.print.check.middle:0
|
||||
#: report:account.print.check.top:0
|
||||
msgid "Original Amount"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: view:res.company:0
|
||||
msgid "Configuration"
|
||||
msgstr "Konfiguracja"
|
||||
|
||||
#. module: account_check_writing
|
||||
#: field:account.voucher,allow_check:0
|
||||
msgid "Allow Check Writing"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: report:account.print.check.bottom:0
|
||||
#: report:account.print.check.middle:0
|
||||
#: report:account.print.check.top:0
|
||||
msgid "Payment"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: field:account.journal,use_preprint_check:0
|
||||
msgid "Use Preprinted Check"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: sql_constraint:res.company:0
|
||||
msgid "The company name must be unique !"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: report:account.print.check.bottom:0
|
||||
#: report:account.print.check.middle:0
|
||||
#: report:account.print.check.top:0
|
||||
msgid "Due Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: model:ir.model,name:account_check_writing.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: view:res.company:0
|
||||
msgid "Default Check Layout"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: constraint:account.journal:0
|
||||
msgid ""
|
||||
"Configuration error! The currency chosen should be shared by the default "
|
||||
"accounts too."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: report:account.print.check.bottom:0
|
||||
#: report:account.print.check.middle:0
|
||||
msgid "Balance Due"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: report:account.print.check.bottom:0
|
||||
#: report:account.print.check.middle:0
|
||||
#: report:account.print.check.top:0
|
||||
msgid "Check Amount"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: model:ir.model,name:account_check_writing.model_account_voucher
|
||||
msgid "Accounting Voucher"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: sql_constraint:account.journal:0
|
||||
msgid "The name of the journal must be unique per company !"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: sql_constraint:account.journal:0
|
||||
msgid "The code of the journal must be unique per company !"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: field:account.voucher,amount_in_word:0
|
||||
msgid "Amount in Word"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: report:account.print.check.top:0
|
||||
msgid "Open Balance"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_check_writing
|
||||
#: field:res.company,check_layout:0
|
||||
msgid "Choose Check layout"
|
||||
msgstr ""
|
|
@ -19,11 +19,14 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
from osv import osv, fields
|
||||
import netsvc
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class payment_mode(osv.osv):
|
||||
_name= 'payment.mode'
|
||||
_description= 'Payment Mode'
|
||||
|
@ -70,9 +73,7 @@ class payment_order(osv.osv):
|
|||
|
||||
#dead code
|
||||
def get_wizard(self, type):
|
||||
logger = netsvc.Logger()
|
||||
logger.notifyChannel("Warning!", netsvc.LOG_WARNING,
|
||||
"No wizard is found for the payment type '%s'." % type)
|
||||
_logger.warning("No wizard found for the payment type '%s'.", type)
|
||||
return None
|
||||
|
||||
def _total(self, cursor, user, ids, name, args, context=None):
|
||||
|
|
|
@ -988,11 +988,11 @@ class account_voucher(osv.osv):
|
|||
if amount_residual > 0:
|
||||
account_id = line.voucher_id.company_id.expense_currency_exchange_account_id
|
||||
if not account_id:
|
||||
raise osv.except_osv(_('Warning!'),_("First you have to configure the 'Expense Currency Rate' on the company, then create accounting entry for currency rate difference."))
|
||||
raise osv.except_osv(_('Insufficient Configuration!'),_("You should configure the 'Loss Exchange Rate Account' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
|
||||
else:
|
||||
account_id = line.voucher_id.company_id.income_currency_exchange_account_id
|
||||
if not account_id:
|
||||
raise osv.except_osv(_('Warning!'),_("First you have to configure the 'Income Currency Rate' on the company, then create accounting entry for currency rate difference."))
|
||||
raise osv.except_osv(_('Insufficient Configuration!'),_("You should configure the 'Gain Exchange Rate Account' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
|
||||
# Even if the amount_currency is never filled, we need to pass the foreign currency because otherwise
|
||||
# the receivable/payable account may have a secondary currency, which render this field mandatory
|
||||
account_currency_id = company_currency <> current_currency and current_currency or False
|
||||
|
|
|
@ -109,8 +109,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -240,8 +240,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -512,8 +512,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<button name="proforma_voucher" string="Validate" states="draft" class="oe_highlight"/>
|
||||
<button name="%(act_pay_voucher)d" context="{'narration':narration, 'title':'Customer Payment', 'type':'receipt', 'partner_id':partner_id, 'reference':reference, 'amount':amount}" type="action" string="Pay" attrs="{'invisible':['|',('pay_now','=','pay_now'),'|',('state','=','draft'), ('paid','=',True)]}" class="oe_highlight"/>
|
||||
<button name="cancel_voucher" string="Cancel" states="draft,proforma" />
|
||||
<button name="cancel_voucher" string="Cancel" type="object" states="posted" confirm="Are you sure to confirm this record ?"/>
|
||||
<button name="cancel_voucher" string="Cancel" type="object" states="posted" confirm="Are you sure you want to cancel this receipt?"/>
|
||||
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,posted" statusbar_colors='{"proforma":"blue"}'/>
|
||||
</header>
|
||||
|
@ -89,7 +89,7 @@
|
|||
<page string="Sales Information">
|
||||
<field name="line_cr_ids" on_change="onchange_price(line_cr_ids, tax_id, partner_id)" context="{'journal_id':journal_id, 'type':type, 'partner_id':partner_id}">
|
||||
<tree string="Sales Lines" editable="bottom">
|
||||
<field name="account_id" domain="[('user_type.report_type','=','income'),('type','!=','view')]" widget="selection" groups="account.group_account_user"/>
|
||||
<field name="account_id" domain="[('user_type.report_type','=','income'),('type','!=','view')]" widget="selection"/>
|
||||
<field name="name"/>
|
||||
<field name="amount" sum="Total"/>
|
||||
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
|
||||
|
@ -147,8 +147,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -209,7 +209,7 @@
|
|||
<button name="proforma_voucher" string="Validate" states="draft" class="oe_highlight"/>
|
||||
<button name="%(act_pay_bills)d" context="{'narration':narration, 'title':'Bill Payment', 'type':'payment', 'partner_id': partner_id, 'reference':reference}" type="action" string="Pay Bill" attrs="{'invisible':['|',('pay_now','=','pay_now'),'|',('state','=','draft'), ('paid','=',True)]}" class="oe_highlight"/>
|
||||
<button name="cancel_voucher" string="Cancel" states="draft,proforma" />
|
||||
<button name="cancel_voucher" string="Cancel" type="object" states="posted" confirm="Are you sure to confirm this record ?"/>
|
||||
<button name="cancel_voucher" string="Cancel" type="object" states="posted" confirm="Are you sure you want to cancel this receipt?"/>
|
||||
<button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,posted" statusbar_colors='{"proforma":"blue"}'/>
|
||||
</header>
|
||||
|
@ -243,7 +243,7 @@
|
|||
<page string="Bill Information">
|
||||
<field name="line_dr_ids" on_change="onchange_price(line_dr_ids, tax_id, partner_id)" context="{'journal_id':journal_id,'partner_id':partner_id}">
|
||||
<tree string="Expense Lines" editable="bottom">
|
||||
<field name="account_id" widget="selection" domain="[('user_type.report_type','=','expense'), ('type','!=','view')]" groups="account.group_account_user"/>
|
||||
<field name="account_id" widget="selection" domain="[('user_type.report_type','=','expense'), ('type','!=','view')]"/>
|
||||
<field name="name"/>
|
||||
<field name="amount"/>
|
||||
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
|
||||
|
@ -303,8 +303,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -96,9 +96,29 @@ class account_analytic_account(osv.osv):
|
|||
res[row['id']][field] = row[field]
|
||||
return self._compute_level_tree(cr, uid, ids, child_ids, res, fields, context)
|
||||
|
||||
def _complete_name_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
|
||||
res = self.name_get(cr, uid, ids)
|
||||
return dict(res)
|
||||
def name_get(self, cr, uid, ids, context=None):
|
||||
res = []
|
||||
for id in ids:
|
||||
elmt = self.browse(cr, uid, id, context=context)
|
||||
res.append((id, self._get_one_full_name(elmt)))
|
||||
return res
|
||||
|
||||
def _get_full_name(self, cr, uid, ids, name=None, args=None, context=None):
|
||||
if context == None:
|
||||
context = {}
|
||||
res = {}
|
||||
for elmt in self.browse(cr, uid, ids, context=context):
|
||||
res[elmt.id] = self._get_one_full_name(elmt)
|
||||
return res
|
||||
|
||||
def _get_one_full_name(self, elmt, level=6):
|
||||
if level<=0:
|
||||
return '...'
|
||||
if elmt.parent_id:
|
||||
parent_path = self._get_one_full_name(elmt.parent_id, level-1) + "/"
|
||||
else:
|
||||
parent_path = ''
|
||||
return parent_path + elmt.name
|
||||
|
||||
def _child_compute(self, cr, uid, ids, name, arg, context=None):
|
||||
result = {}
|
||||
|
@ -139,13 +159,13 @@ class account_analytic_account(osv.osv):
|
|||
|
||||
_columns = {
|
||||
'name': fields.char('Account/Contract Name', size=128, required=True),
|
||||
'complete_name': fields.function(_complete_name_calc, type='char', string='Full Account Name'),
|
||||
'complete_name': fields.function(_get_full_name, type='char', string='Full Account Name'),
|
||||
'code': fields.char('Reference', size=24, select=True),
|
||||
'type': fields.selection([('view','Analytic View'), ('normal','Analytic Account'),('contract','Contract or Project'),('template','Template of Project')], 'Type of Account', required=True,
|
||||
'type': fields.selection([('view','Analytic View'), ('normal','Analytic Account'),('contract','Contract or Project'),('template','Template of Contract')], 'Type of Account', required=True,
|
||||
help="If you select the View Type, it means you won\'t allow to create journal entries using that account.\n"\
|
||||
"The type 'Analytic account' stands for usual accounts that you only want to use in accounting.\n"\
|
||||
"If you select Contract or Project, it offers you the possibility to manage the validity and the invoicing options for this account.\n"\
|
||||
"The special type 'Template of Project' allows you to define a template with default data that you can reuse easily."),
|
||||
"The special type 'Template of Contract' allows you to define a template with default data that you can reuse easily."),
|
||||
'template_id': fields.many2one('account.analytic.account', 'Template of Contract'),
|
||||
'description': fields.text('Description'),
|
||||
'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2),
|
||||
|
@ -156,7 +176,7 @@ class account_analytic_account(osv.osv):
|
|||
'debit': fields.function(_debit_credit_bal_qtty, type='float', string='Debit', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')),
|
||||
'credit': fields.function(_debit_credit_bal_qtty, type='float', string='Credit', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')),
|
||||
'quantity': fields.function(_debit_credit_bal_qtty, type='float', string='Quantity', multi='debit_credit_bal_qtty'),
|
||||
'quantity_max': fields.float('Prepaid Units', help='Sets the higher limit of time to work on the contract.'),
|
||||
'quantity_max': fields.float('Prepaid Service Units', help='Sets the higher limit of time to work on the contract, based on the timesheet. (for instance, number of hours in a limited support contract.)'),
|
||||
'partner_id': fields.many2one('res.partner', 'Customer'),
|
||||
'user_id': fields.many2one('res.users', 'Project Manager'),
|
||||
'manager_id': fields.many2one('res.users', 'Account Manager'),
|
||||
|
@ -305,8 +325,15 @@ class account_analytic_line(osv.osv):
|
|||
'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
|
||||
|
||||
}
|
||||
|
||||
def _get_default_date(self, cr, uid, context=None):
|
||||
return fields.date.context_today(self, cr, uid, context=context)
|
||||
|
||||
def __get_default_date(self, cr, uid, context=None):
|
||||
return self._get_default_date(cr, uid, context=context)
|
||||
|
||||
_defaults = {
|
||||
'date': lambda *a: time.strftime('%Y-%m-%d'),
|
||||
'date': __get_default_date,
|
||||
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context=c),
|
||||
'amount': 0.00
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
<group name="main">
|
||||
<group>
|
||||
<field name="partner_id" on_change="on_change_partner_id(partner_id, name)" attrs="{'required':[('type','=','contract')]}"/>
|
||||
<field name="partner_id" on_change="on_change_partner_id(partner_id, name)"/>
|
||||
<field name="manager_id"/>
|
||||
<field name="code"/>
|
||||
<field name="currency_id" attrs="{'invisible': ['|',('type', '<>', 'view'), ('company_id', '<>', False)]}"/>
|
||||
|
@ -39,8 +39,10 @@
|
|||
<p colspan="2" class="oe_grey oe_edit_only">
|
||||
Once the end date of the contract is
|
||||
passed or the maximum number of service
|
||||
units is reached, the account manager
|
||||
is warned by email to renew the contract.
|
||||
units (e.g. support contract) is
|
||||
reached, the account manager is warned
|
||||
by email to renew the contract with the
|
||||
customer.
|
||||
</p>
|
||||
<field name="date_start"/>
|
||||
<label for="date" string="End Date"/>
|
||||
|
@ -54,8 +56,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
<field name="arch" type="xml">
|
||||
<xpath expr='//tr[@name="total"]' position='before'>
|
||||
<tr>
|
||||
<td class="oe_grey">
|
||||
<td class="oe_timesheet_grey">
|
||||
<label for="charge_expenses"/>
|
||||
</td><td class="oe_grey">
|
||||
</td><td class="oe_timesheet_grey">
|
||||
<field name="charge_expenses"/>
|
||||
</td><td>
|
||||
<field class="oe_form_inline" name="est_expenses" attrs="{'invisible': [('charge_expenses','=',False)]}"/>
|
||||
|
@ -21,17 +21,22 @@
|
|||
<field class="oe_form_inline" name="remaining_expense" attrs="{'invisible': [('charge_expenses','=',False)]}"/>
|
||||
</td><td>
|
||||
<field class="oe_form_inline" name="expense_to_invoice" attrs="{'invisible': [('charge_expenses','=',False)]}"/>
|
||||
</td><td>
|
||||
<button
|
||||
name="hr_to_invoice_expense"
|
||||
class="oe_link oe_e"
|
||||
string="/" type="object"
|
||||
attrs="{'invisible': [('expense_to_invoice','=',0)]}"/>
|
||||
</td><td>
|
||||
</td><td attrs="{'invisible': [('charge_expenses','=',False)]}" class="oe_timesheet_action">
|
||||
<span attrs="{'invisible': [('expense_to_invoice','=',0)]}" class="oe_grey">
|
||||
<button
|
||||
name="hr_to_invoice_expense"
|
||||
class="oe_link"
|
||||
string="⇒ Invoice" type="object"
|
||||
/>
|
||||
or view
|
||||
</span>
|
||||
<span attrs="{'invisible': [('expense_to_invoice','<>',0)]}" class="oe_grey">
|
||||
Nothing to invoice, create
|
||||
</span>
|
||||
<button
|
||||
name="open_hr_expense"
|
||||
class="oe_link"
|
||||
string="Expenses" type="object" attrs="{'invisible': [('charge_expenses','=',False)]}"/>
|
||||
string="expenses" type="object"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xpath>
|
||||
|
|
|
@ -21,39 +21,51 @@
|
|||
|
||||
from osv import fields,osv
|
||||
from tools.translate import _
|
||||
import decimal_precision as dp
|
||||
|
||||
class analytic_user_funct_grid(osv.osv):
|
||||
|
||||
_name="analytic.user.funct.grid"
|
||||
_description= "Relation table between users and products on a analytic account"
|
||||
_description= "Price per User"
|
||||
_columns={
|
||||
'user_id': fields.many2one("res.users", "User", required=True,),
|
||||
'product_id': fields.many2one("product.product", "Product", required=True,),
|
||||
'product_id': fields.many2one("product.product", "Service", required=True,),
|
||||
'account_id': fields.many2one("account.analytic.account", "Analytic Account", required=True,),
|
||||
}
|
||||
'uom_id': fields.related("product_id", "uom_id", relation="product.uom", string="Unit of Measure", type="many2one", readonly=True),
|
||||
'price': fields.float('Price', digits_compute=dp.get_precision('Product Price'), help="Price per hour for this user.", required=True),
|
||||
}
|
||||
def onchange_user_product_id(self, cr, uid, ids, user_id, product_id, context=None):
|
||||
if not user_id:
|
||||
return {}
|
||||
emp_obj = self.pool.get('hr.employee')
|
||||
emp_id = emp_obj.search(cr, uid, [('user_id', '=', user_id)], context=context)
|
||||
if not emp_id:
|
||||
return {}
|
||||
|
||||
analytic_user_funct_grid()
|
||||
value = {}
|
||||
emp = emp_obj.browse(cr, uid, emp_id[0], context=context)
|
||||
if emp.product_id and not product_id:
|
||||
value['product_id'] = emp.product_id.id
|
||||
prod = emp.product_id
|
||||
if product_id:
|
||||
prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
|
||||
if prod:
|
||||
value['price'] = prod.list_price
|
||||
value['uom_id'] = prod.uom_id.id
|
||||
return {'value': value}
|
||||
|
||||
|
||||
class account_analytic_account(osv.osv):
|
||||
|
||||
_inherit = "account.analytic.account"
|
||||
_columns = {
|
||||
'user_product_ids': fields.one2many('analytic.user.funct.grid', 'account_id', 'Users/Products Rel.'),
|
||||
}
|
||||
|
||||
account_analytic_account()
|
||||
|
||||
|
||||
class hr_analytic_timesheet(osv.osv):
|
||||
|
||||
_inherit = "hr.analytic.timesheet"
|
||||
|
||||
|
||||
# Look in account, if no value for the user => look in parent until there is no more parent to look
|
||||
# Take the first found... if nothing found => return False
|
||||
def _get_related_user_account_recursiv(self, cr, uid, user_id, account_id):
|
||||
|
||||
temp=self.pool.get('analytic.user.funct.grid').search(cr, uid, [('user_id', '=', user_id),('account_id', '=', account_id) ])
|
||||
account=self.pool.get('account.analytic.account').browse(cr, uid, account_id)
|
||||
if temp:
|
||||
|
@ -64,7 +76,6 @@ class hr_analytic_timesheet(osv.osv):
|
|||
else:
|
||||
return False
|
||||
|
||||
|
||||
def on_change_account_id(self, cr, uid, ids, account_id, user_id=False, unit_amount=0):
|
||||
res = {}
|
||||
if not (account_id):
|
||||
|
@ -106,12 +117,6 @@ class hr_analytic_timesheet(osv.osv):
|
|||
return res
|
||||
|
||||
def on_change_user_id(self, cr, uid, ids, user_id, account_id, unit_amount=0):
|
||||
res = {}
|
||||
if not (user_id):
|
||||
#avoid a useless call to super
|
||||
return res
|
||||
|
||||
#get the old values from super
|
||||
res = super(hr_analytic_timesheet, self).on_change_user_id(cr, uid, ids, user_id)
|
||||
|
||||
if account_id:
|
||||
|
@ -141,7 +146,11 @@ class hr_analytic_timesheet(osv.osv):
|
|||
res ['value']['general_account_id']= a
|
||||
return res
|
||||
|
||||
hr_analytic_timesheet()
|
||||
class account_analytic_line(osv.osv):
|
||||
_inherit = "account.analytic.line"
|
||||
def _get_invoice_price(self, cr, uid, account, product_id, user_id, qty, context = {}):
|
||||
for grid in account.user_product_ids:
|
||||
if grid.user_id.id==user_id:
|
||||
return grid.price
|
||||
return super(account_analytic_line, self)._get_invoice_price(cr, uid, account, product_id, user_id, qty, context)
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
<field name="name">analytic_user_funct_grid.tree</field>
|
||||
<field name="model">analytic.user.funct.grid</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="User's Product for this Analytic Account" editable="bottom">
|
||||
<field name="user_id" required="1"/>
|
||||
<field name="product_id" required="1" />
|
||||
<tree string="Invoicing Data" editable="bottom">
|
||||
<field name="user_id" on_change="onchange_user_product_id(user_id, product_id)"/>
|
||||
<field name="product_id" on_change="onchange_user_product_id(user_id, product_id)" domain="[('type','=','service')]"/>
|
||||
<field name="price"/>
|
||||
<field name="uom_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -18,9 +20,13 @@
|
|||
<field name="name">analytic_user_funct_grid.form</field>
|
||||
<field name="model">analytic.user.funct.grid</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="User's Product for this Analytic Account">
|
||||
<field name="user_id" required="1"/>
|
||||
<field name="product_id" required="1"/>
|
||||
<form string="Invoicing Data">
|
||||
<group>
|
||||
<field name="user_id" on_change="onchange_user_product_id(user_id, product_id)"/>
|
||||
<field name="product_id" domain="[('type','=','service')]" on_change="onchange_user_product_id(user_id, product_id)"/>
|
||||
<field name="price"/>
|
||||
<field name="uom_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -29,13 +35,26 @@
|
|||
<record model="ir.ui.view" id="view_account_analytic_account_form_inherit">
|
||||
<field name="name">account.analytic.account.form</field>
|
||||
<field name="model">account.analytic.account</field>
|
||||
<field eval="60" name="priority"/>
|
||||
<field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//notebook/page" position="after">
|
||||
<page string="Users/Products Rel.">
|
||||
<field name="user_product_ids" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</xpath>
|
||||
<separator name="description" position="before">
|
||||
<div name="user_function_price" attrs="{'invisible': [('invoice_on_timesheets','=',False)]}">
|
||||
<separator string="Invoice Price Rate per User"/>
|
||||
<p class="oe_grey oe_edit_only">
|
||||
Define a specific service (e.g. Senior Consultant)
|
||||
and price for some users to use these data instead
|
||||
of the default values when invoicing the customer.
|
||||
</p>
|
||||
<p class="oe_grey oe_edit_only">
|
||||
OpenERP will recursively search on parent accounts
|
||||
to check if specific conditions are defined for a
|
||||
specific user. This allows to set invoicing
|
||||
conditions for a group of contracts.
|
||||
</p>
|
||||
<field name="user_product_ids"/>
|
||||
</div>
|
||||
</separator>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
# Polish translation for openobject-addons
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openobject-addons package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-08 01:37+0100\n"
|
||||
"PO-Revision-Date: 2012-10-25 17:11+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. #-#-#-#-# auth_openid.pot (OpenERP Server 6.1rc1) #-#-#-#-#
|
||||
#. module: auth_openid
|
||||
#. #-#-#-#-# auth_openid.pot.web (PROJECT VERSION) #-#-#-#-#
|
||||
#. openerp-web
|
||||
#: view:res.users:0
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:12
|
||||
msgid "OpenID"
|
||||
msgstr ""
|
||||
|
||||
#. #-#-#-#-# auth_openid.pot (OpenERP Server 6.1rc1) #-#-#-#-#
|
||||
#. module: auth_openid
|
||||
#. #-#-#-#-# auth_openid.pot.web (PROJECT VERSION) #-#-#-#-#
|
||||
#. openerp-web
|
||||
#: field:res.users,openid_url:0
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:47
|
||||
msgid "OpenID URL"
|
||||
msgstr ""
|
||||
|
||||
#. module: auth_openid
|
||||
#: help:res.users,openid_email:0
|
||||
msgid "Used for disambiguation in case of a shared OpenID URL"
|
||||
msgstr ""
|
||||
|
||||
#. module: auth_openid
|
||||
#: sql_constraint:res.users:0
|
||||
msgid "You can not have two users with the same login !"
|
||||
msgstr ""
|
||||
|
||||
#. module: auth_openid
|
||||
#: field:res.users,openid_email:0
|
||||
msgid "OpenID Email"
|
||||
msgstr ""
|
||||
|
||||
#. module: auth_openid
|
||||
#: constraint:res.users:0
|
||||
msgid "The chosen company is not in the allowed companies for this user"
|
||||
msgstr ""
|
||||
|
||||
#. module: auth_openid
|
||||
#: field:res.users,openid_key:0
|
||||
msgid "OpenID Key"
|
||||
msgstr ""
|
||||
|
||||
#. module: auth_openid
|
||||
#: model:ir.model,name:auth_openid.model_res_users
|
||||
msgid "res.users"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:8
|
||||
msgid "Password"
|
||||
msgstr "Hasło"
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:9
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:10
|
||||
msgid "Google"
|
||||
msgstr "Google"
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:10
|
||||
msgid "Google Apps"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:11
|
||||
msgid "Launchpad"
|
||||
msgstr "Launchpad"
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:20
|
||||
msgid "Google Apps Domain:"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:24
|
||||
msgid "Username:"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:28
|
||||
msgid "OpenID URL:"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:35
|
||||
msgid "Google Apps Domain"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: /home/odo/repositories/addons/trunk/auth_openid/static/src/xml/auth_openid.xml:41
|
||||
msgid "Username"
|
||||
msgstr ""
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
|
||||
"PO-Revision-Date: 2012-05-10 17:50+0000\n"
|
||||
"Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.com>\n"
|
||||
"PO-Revision-Date: 2012-10-25 16:32+0000\n"
|
||||
"Last-Translator: ccdos <Unknown>\n"
|
||||
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:35+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: base_action_rule
|
||||
#: help:base.action.rule,act_mail_to_user:0
|
||||
|
@ -315,7 +315,7 @@ msgstr "激活"
|
|||
#: code:addons/base_action_rule/base_action_rule.py:329
|
||||
#, python-format
|
||||
msgid "No Email ID Found for your Company address!"
|
||||
msgstr ""
|
||||
msgstr "公司地址中没有设置Email !"
|
||||
|
||||
#. module: base_action_rule
|
||||
#: field:base.action.rule,act_remind_user:0
|
||||
|
|
|
@ -1151,7 +1151,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
|
|||
context = {}
|
||||
|
||||
result = []
|
||||
for data in super(calendar_event, self).read(cr, uid, select, context=context):
|
||||
for data in super(calendar_event, self).read(cr, uid, select, ['rrule', 'exdate', 'exrule', 'date'], context=context):
|
||||
if not data['rrule']:
|
||||
result.append(data['id'])
|
||||
continue
|
||||
|
|
|
@ -170,12 +170,7 @@
|
|||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<!--
|
||||
Temporarily removing invitation feature as the implementation
|
||||
was not clean. Invitation should be trigerred automatically
|
||||
based on partner_ids.
|
||||
-->
|
||||
<page string="Invitations">
|
||||
<page string="Invitations" groups="base.group_no_one">
|
||||
<field name="attendee_ids" widget="one2many" mode="tree">
|
||||
<tree string="Invitation details" editable="top">
|
||||
<field name="partner_id"/>
|
||||
|
@ -224,8 +219,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -333,9 +328,6 @@
|
|||
<field name="view_id" ref="view_crm_meeting_gantt"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_crm_meeting" parent="base.menu_sales" sequence="8"
|
||||
name="Calendar" action="action_crm_meeting"/>
|
||||
|
||||
<menuitem name="Calendar"
|
||||
id="mail_menu_calendar" parent="mail.mail_my_stuff"
|
||||
sequence="10" action="action_crm_meeting"/>
|
||||
|
|
|
@ -7,14 +7,14 @@ msgstr ""
|
|||
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
|
||||
"PO-Revision-Date: 2010-10-30 10:01+0000\n"
|
||||
"Last-Translator: OpenERP Administrators <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-10-26 09:05+0000\n"
|
||||
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:20+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-27 04:59+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: board
|
||||
#: view:res.log.report:0
|
||||
|
@ -103,7 +103,7 @@ msgstr "Месячная активность на документ"
|
|||
#. module: board
|
||||
#: view:board.board:0
|
||||
msgid "Configuration Overview"
|
||||
msgstr ""
|
||||
msgstr "Обзор конфигурации"
|
||||
|
||||
#. module: board
|
||||
#: model:ir.actions.act_window,name:board.action_view_board_list_form
|
||||
|
@ -211,7 +211,7 @@ msgstr "Январь"
|
|||
#. module: board
|
||||
#: view:board.board:0
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
msgstr "Пользователи"
|
||||
|
||||
#. module: board
|
||||
#: selection:res.log.report,month:0
|
||||
|
@ -262,7 +262,7 @@ msgstr "Модель"
|
|||
#. module: board
|
||||
#: model:ir.actions.act_window,name:board.board_homepage_action
|
||||
msgid "Home Page"
|
||||
msgstr ""
|
||||
msgstr "Домашняя страница"
|
||||
|
||||
#. module: board
|
||||
#: model:ir.actions.act_window,name:board.action_latest_activities_tree
|
||||
|
|
|
@ -86,7 +86,6 @@ Dashboard for CRM will include:
|
|||
'crm_lead_menu.xml',
|
||||
|
||||
'crm_meeting_menu.xml',
|
||||
'crm_meeting_shortcut_data.xml',
|
||||
|
||||
'crm_phonecall_view.xml',
|
||||
'crm_phonecall_menu.xml',
|
||||
|
|
|
@ -6,13 +6,6 @@
|
|||
<field name="groups_id" eval="[(4,ref('base.group_sale_salesman'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ir_ui_view_sc_calendar_demo" model="ir.ui.view_sc">
|
||||
<field name="name">Meetings</field>
|
||||
<field name="resource">ir.ui.menu</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="res_id" ref="base_calendar.menu_crm_meeting"/>
|
||||
</record>
|
||||
|
||||
<record model="crm.case.section" id="crm_case_section_1">
|
||||
<field name="name">Sales Marketing Department</field>
|
||||
<field name="code">SMD</field>
|
||||
|
|
|
@ -223,8 +223,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -525,8 +525,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="ir_ui_view_sc_calendar0" model="ir.ui.view_sc">
|
||||
<field name="name">Meetings</field>
|
||||
<field name="resource">ir.ui.menu</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="res_id" ref="base_calendar.menu_crm_meeting"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -125,7 +125,7 @@
|
|||
</div>
|
||||
<div class="oe_title">
|
||||
<div class="oe_edit_only">
|
||||
<label for="name" string="Title"/>
|
||||
<label for="name"/>
|
||||
</div>
|
||||
<h1><field name="name" required="1"/></h1>
|
||||
<div class="oe_edit_only">
|
||||
|
@ -150,8 +150,8 @@
|
|||
<field name="description" placeholder="Description..."/>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -132,8 +132,8 @@
|
|||
</page>
|
||||
</notebook>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers" help="Followers of this salesteam follow automatically all opportunities related to this salesteam."/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -7,14 +7,14 @@ msgstr ""
|
|||
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-02-08 01:37+0100\n"
|
||||
"PO-Revision-Date: 2012-03-22 16:17+0000\n"
|
||||
"Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.com>\n"
|
||||
"PO-Revision-Date: 2012-10-25 16:22+0000\n"
|
||||
"Last-Translator: fenshuajiang <openerp@126.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-08-28 06:06+0000\n"
|
||||
"X-Generator: Launchpad (build 15864)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: crm
|
||||
#: view:crm.lead.report:0
|
||||
|
@ -172,7 +172,7 @@ msgstr "预计结束月份"
|
|||
#. module: crm
|
||||
#: view:crm.lead2opportunity.partner.mass:0
|
||||
msgid "Assigned Opportunities to"
|
||||
msgstr ""
|
||||
msgstr "分配商机给"
|
||||
|
||||
#. module: crm
|
||||
#: view:crm.lead:0 field:crm.lead,partner_id:0 view:crm.lead.report:0
|
||||
|
@ -583,7 +583,7 @@ msgstr "结束日期"
|
|||
#. module: crm
|
||||
#: view:crm.opportunity2phonecall:0 view:crm.phonecall2phonecall:0
|
||||
msgid "Schedule/Log a Call"
|
||||
msgstr ""
|
||||
msgstr "计划/记录一个电话"
|
||||
|
||||
#. module: crm
|
||||
#: constraint:base.action.rule:0
|
||||
|
@ -763,7 +763,7 @@ msgstr "继续"
|
|||
#. module: crm
|
||||
#: field:crm.segmentation,som_interval:0
|
||||
msgid "Days per Period"
|
||||
msgstr ""
|
||||
msgstr "每阶段的天数"
|
||||
|
||||
#. module: crm
|
||||
#: field:crm.meeting,byday:0
|
||||
|
@ -926,7 +926,7 @@ msgstr "开启天数"
|
|||
#. module: crm
|
||||
#: view:crm.meeting:0
|
||||
msgid "Show Time as"
|
||||
msgstr ""
|
||||
msgstr "显示时间为"
|
||||
|
||||
#. module: crm
|
||||
#: view:crm.phonecall2partner:0
|
||||
|
@ -1299,7 +1299,7 @@ msgstr "写日期"
|
|||
#. module: crm
|
||||
#: view:crm.meeting:0
|
||||
msgid "End of Recurrency"
|
||||
msgstr ""
|
||||
msgstr "结束循环"
|
||||
|
||||
#. module: crm
|
||||
#: view:crm.meeting:0
|
||||
|
@ -1835,7 +1835,7 @@ msgstr "回复到"
|
|||
#. module: crm
|
||||
#: view:crm.case.section:0
|
||||
msgid "Select Stages for this Sales Team"
|
||||
msgstr ""
|
||||
msgstr "为这个销售团队选择阶段"
|
||||
|
||||
#. module: crm
|
||||
#: view:board.board:0
|
||||
|
@ -2683,7 +2683,7 @@ msgstr "这联系的邮件地址"
|
|||
#. module: crm
|
||||
#: field:crm.lead,referred:0
|
||||
msgid "Referred by"
|
||||
msgstr ""
|
||||
msgstr "推荐人"
|
||||
|
||||
#. module: crm
|
||||
#: view:crm.lead:0 model:ir.model,name:crm.model_crm_add_note
|
||||
|
|
|
@ -179,8 +179,8 @@
|
|||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# Polish translation for openobject-addons
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openobject-addons package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
|
||||
"PO-Revision-Date: 2012-10-25 16:50+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: decimal_precision
|
||||
#: field:decimal.precision,digits:0
|
||||
msgid "Digits"
|
||||
msgstr "Cyfry"
|
||||
|
||||
#. module: decimal_precision
|
||||
#: model:ir.actions.act_window,name:decimal_precision.action_decimal_precision_form
|
||||
#: model:ir.ui.menu,name:decimal_precision.menu_decimal_precision_form
|
||||
msgid "Decimal Accuracy"
|
||||
msgstr ""
|
||||
|
||||
#. module: decimal_precision
|
||||
#: field:decimal.precision,name:0
|
||||
msgid "Usage"
|
||||
msgstr "Użycie"
|
||||
|
||||
#. module: decimal_precision
|
||||
#: sql_constraint:decimal.precision:0
|
||||
msgid "Only one value can be defined for each given usage!"
|
||||
msgstr ""
|
||||
|
||||
#. module: decimal_precision
|
||||
#: view:decimal.precision:0
|
||||
msgid "Decimal Precision"
|
||||
msgstr ""
|
||||
|
||||
#. module: decimal_precision
|
||||
#: model:ir.model,name:decimal_precision.model_decimal_precision
|
||||
msgid "decimal.precision"
|
||||
msgstr ""
|
|
@ -103,7 +103,7 @@ class DocIndex(indexer):
|
|||
return _to_unicode(data)
|
||||
except OSError:
|
||||
|
||||
_logger.warn("Failed attempt to execute antiword (MS Word reader). Antiword is necessary to index the file %s of MIME type %s. Detailed error available at DEBUG level.", fname, self._getMimeTypes()[0])
|
||||
_logger.warning("Failed attempt to execute antiword (MS Word reader). Antiword is necessary to index the file %s of MIME type %s. Detailed error available at DEBUG level.", fname, self._getMimeTypes()[0])
|
||||
_logger.debug("Trace of the failed file indexing attempt.", exc_info=True)
|
||||
return False
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ _logger = logging.getLogger(__name__)
|
|||
try:
|
||||
import controllers
|
||||
except ImportError:
|
||||
_logger.warn(
|
||||
_logger.warning(
|
||||
"""Could not load openerp-web section of EDI, EDI will not behave correctly
|
||||
|
||||
To fix, launch openerp-web in embedded mode""")
|
||||
|
|
|
@ -165,11 +165,8 @@ class test_message_compose(test_mail.TestMailMockups):
|
|||
self.assertEqual(message_pigs.body, _body_html1, 'mail.message body on Pigs incorrect')
|
||||
self.assertEqual(message_bird.body, _body_html2, 'mail.message body on Bird incorrect')
|
||||
# Test: partner_ids: p_a_id (default) + 3 newly created partners
|
||||
message_pigs_pids = [partner.id for partner in message_pigs.partner_ids]
|
||||
message_bird_pids = [partner.id for partner in message_bird.partner_ids]
|
||||
message_pigs_pids = [partner.id for partner in message_pigs.notified_partner_ids]
|
||||
message_bird_pids = [partner.id for partner in message_bird.notified_partner_ids]
|
||||
partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])])
|
||||
self.assertEqual(len(message_pigs_pids), len(partner_ids), 'mail.message on pigs incorrect number of partner_ids')
|
||||
self.assertEqual(set(message_pigs_pids), set(partner_ids), 'mail.message on pigs incorrect number of partner_ids')
|
||||
|
||||
self.assertEqual(len(message_bird_pids), len(partner_ids), 'mail.message on bird partner_ids incorrect')
|
||||
self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird partner_ids incorrect')
|
||||
self.assertEqual(set(message_pigs_pids), set(partner_ids), 'mail.message on pigs incorrect number of notified_partner_ids')
|
||||
self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird notified_partner_ids incorrect')
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//form/notebook" position="after">
|
||||
<xpath expr="//form/group" position="after">
|
||||
<group attrs="{'invisible':[('use_template','=',False)]}">
|
||||
<field name="use_template" invisible="1"
|
||||
on_change="onchange_use_template(use_template, template_id, composition_mode, model, res_id, context)"/>
|
||||
|
|
|
@ -204,8 +204,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -486,8 +486,8 @@
|
|||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -112,7 +112,7 @@ class hr_job(osv.osv):
|
|||
'requirements': fields.text('Requirements'),
|
||||
'department_id': fields.many2one('hr.department', 'Department'),
|
||||
'company_id': fields.many2one('res.company', 'Company'),
|
||||
'state': fields.selection([('open', 'In Position'), ('recruit', 'In Recruitement')], 'Status', readonly=True, required=True,
|
||||
'state': fields.selection([('open', 'No Recruitment'), ('recruit', 'Recruitement in Progress')], 'Status', readonly=True, required=True,
|
||||
help="By default 'In position', set it to 'In Recruitment' if recruitment process is going on for this job position."),
|
||||
}
|
||||
_defaults = {
|
||||
|
|
|
@ -330,8 +330,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<form string="Job" version="7.0">
|
||||
<header>
|
||||
<button name="job_recruitement" string="In Recruitement" states="open" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="job_open" string="Recruitment Done" states="recruit" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="job_recruitement" string="Launch Recruitement" states="open" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="job_open" string="Stop Recruitment" states="recruit" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="recruit,open"/>
|
||||
</header>
|
||||
<sheet>
|
||||
|
@ -359,8 +359,8 @@
|
|||
</div>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -7,14 +7,14 @@ msgstr ""
|
|||
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-02-08 01:37+0100\n"
|
||||
"PO-Revision-Date: 2012-08-18 17:32+0000\n"
|
||||
"Last-Translator: Heling Yao <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-10-25 16:29+0000\n"
|
||||
"Last-Translator: fenshuajiang <openerp@126.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: hr
|
||||
#: model:process.node,name:hr.process_node_openerpuser0
|
||||
|
@ -181,7 +181,7 @@ msgstr "女性"
|
|||
#: help:hr.job,expected_employees:0
|
||||
msgid ""
|
||||
"Expected number of employees for this job position after new recruitment."
|
||||
msgstr ""
|
||||
msgstr "新的招聘后这个工作岗位上期望的员工人数"
|
||||
|
||||
#. module: hr
|
||||
#: model:ir.ui.menu,name:hr.menu_open_view_attendance_reason_new_config
|
||||
|
@ -685,7 +685,7 @@ msgstr "下属"
|
|||
#. module: hr
|
||||
#: field:hr.job,no_of_employee:0
|
||||
msgid "Number of employees currently occupying this job position."
|
||||
msgstr ""
|
||||
msgstr "这个工作岗位上现有的员工人数"
|
||||
|
||||
#. module: hr
|
||||
#: field:hr.job,no_of_recruitment:0
|
||||
|
|
|
@ -28,7 +28,7 @@ class hr_config_settings(osv.osv_memory):
|
|||
_columns = {
|
||||
'module_hr_timesheet_sheet': fields.boolean('Allow timesheets validation by managers',
|
||||
help ="""This installs the module hr_timesheet_sheet."""),
|
||||
'module_hr_attendance': fields.boolean('Track attendances',
|
||||
'module_hr_attendance': fields.boolean('Install attendances feature',
|
||||
help ="""This installs the module hr_attendance."""),
|
||||
'module_hr_timesheet': fields.boolean('Manage timesheets',
|
||||
help ="""This installs the module hr_timesheet."""),
|
||||
|
@ -42,6 +42,8 @@ class hr_config_settings(osv.osv_memory):
|
|||
help ="""This installs the module hr_contract."""),
|
||||
'module_hr_evaluation': fields.boolean('Organize employees periodic evaluation',
|
||||
help ="""This installs the module hr_evaluation."""),
|
||||
'module_account_analytic_analysis': fields.boolean('Allow invoicing based on timesheets (the sale application will be installed)',
|
||||
help ="""This installs the module account_analytic_analysis, which will install sales management too."""),
|
||||
'module_hr_payroll': fields.boolean('Manage payroll',
|
||||
help ="""This installs the module hr_payroll."""),
|
||||
}
|
||||
|
|
|
@ -69,6 +69,10 @@
|
|||
<field name="module_hr_timesheet_sheet" class="oe_inline"/>
|
||||
<label for="module_hr_timesheet_sheet"/>
|
||||
</div>
|
||||
<div name="account_analytic_analysis">
|
||||
<field name="module_account_analytic_analysis" class="oe_inline"/>
|
||||
<label for="module_account_analytic_analysis"/>
|
||||
</div>
|
||||
<div name="hr_attendance">
|
||||
<field name="module_hr_attendance" on_change="onchange_hr_attendance(module_hr_attendance)" class="oe_inline"/>
|
||||
<label for="module_hr_attendance"/>
|
||||
|
|
|
@ -25,7 +25,7 @@ class hr_attendance_config_settings(osv.osv_memory):
|
|||
_inherit = 'hr.config.settings'
|
||||
|
||||
_columns = {
|
||||
'group_hr_attendance': fields.boolean('Track attendances',
|
||||
'group_hr_attendance': fields.boolean('Track attendances for all employees',
|
||||
implied_group='base.group_hr_attendance',
|
||||
help="Allocates attendance group to all users."),
|
||||
}
|
||||
|
|
|
@ -210,8 +210,8 @@
|
|||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -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-05-10 17:47+0000\n"
|
||||
"Last-Translator: Jeff Wang <wjfonhand@hotmail.com>\n"
|
||||
"PO-Revision-Date: 2012-10-25 17:16+0000\n"
|
||||
"Last-Translator: ccdos <Unknown>\n"
|
||||
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:27+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: hr_evaluation
|
||||
#: help:hr_evaluation.plan.phase,send_anonymous_manager:0
|
||||
|
@ -304,7 +304,7 @@ msgstr "上个月完成的评估"
|
|||
#. module: hr_evaluation
|
||||
#: model:ir.model,name:hr_evaluation.model_mail_compose_message
|
||||
msgid "Email composition wizard"
|
||||
msgstr ""
|
||||
msgstr "电子邮件撰写向导"
|
||||
|
||||
#. module: hr_evaluation
|
||||
#: view:hr.evaluation.report:0
|
||||
|
|
|
@ -64,15 +64,15 @@ class hr_expense_expense(osv.osv):
|
|||
_description = "Expense"
|
||||
_order = "id desc"
|
||||
_columns = {
|
||||
'name': fields.char('Description', size=128, required=True),
|
||||
'name': fields.char('Description', size=128),
|
||||
'id': fields.integer('Sheet ID', readonly=True),
|
||||
'date': fields.date('Date', select=True),
|
||||
'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is done."),
|
||||
'employee_id': fields.many2one('hr.employee', "Employee", required=True),
|
||||
'user_id': fields.many2one('res.users', 'User', required=True),
|
||||
'date_confirm': fields.date('Confirmation Date', select=True, help = "Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."),
|
||||
'date_valid': fields.date('Validation Date', select=True, help = "Date of the acceptation of the sheet expense. It's filled when the button Accept is pressed."),
|
||||
'user_valid': fields.many2one('res.users', 'Validation User'),
|
||||
'date_confirm': fields.date('Confirmation Date', select=True, help="Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."),
|
||||
'date_valid': fields.date('Validation Date', select=True, help="Date of the acceptation of the sheet expense. It's filled when the button Accept is pressed."),
|
||||
'user_valid': fields.many2one('res.users', 'Validation By'),
|
||||
'account_move_id': fields.many2one('account.move', 'Ledger Posting'),
|
||||
'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ),
|
||||
'note': fields.text('Note'),
|
||||
|
@ -189,7 +189,7 @@ class hr_expense_expense(osv.osv):
|
|||
raise osv.except_osv(_('Error!'), _('The employee must have a home address.'))
|
||||
acc = exp.employee_id.address_home_id.property_account_payable.id
|
||||
voucher = {
|
||||
'name': exp.name,
|
||||
'name': exp.name or '/',
|
||||
'reference': sequence_obj.get(cr, uid, 'hr.expense.invoice'),
|
||||
'account_id': acc,
|
||||
'type': 'purchase',
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
</group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="user_valid"/>
|
||||
<field name="user_valid" attrs="{'invisible': [('state','=','draft')]}"/>
|
||||
<field name="currency_id" groups="base.group_multi_currency" on_change="onchange_currency_id(currency_id, company_id)"/>
|
||||
</group>
|
||||
</group>
|
||||
|
@ -128,9 +128,9 @@
|
|||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Other Info">
|
||||
<page string="Accounting" groups="account.group_account_user">
|
||||
<group>
|
||||
<group string="Accounting Data" groups="account.group_account_user">
|
||||
<group string="Accounting Data">
|
||||
<field name="journal_id" widget="selection" domain="[('type', '=', 'purchase')]"/>
|
||||
<field name="voucher_id" context="{'form_view_ref': 'account_voucher.view_purchase_receipt_form'}"/>
|
||||
</group>
|
||||
|
@ -139,8 +139,8 @@
|
|||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -121,8 +121,8 @@
|
|||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -158,8 +158,8 @@
|
|||
<field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..."/>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
<form string="Jobs - Recruitment Form" version="7.0">
|
||||
<header>
|
||||
<button name="case_close_with_emp" string="Hire" type="object"
|
||||
states="draft,open,pending" class="oe_highlight"/>
|
||||
states="draft,open,pending,done" class="oe_highlight"/>
|
||||
<button name="case_cancel" string="Refuse" type="object"
|
||||
states="draft,open,pending" class="oe_highlight"/>
|
||||
<field name="stage_id" widget="statusbar" clickable="True"/>
|
||||
|
@ -183,8 +183,8 @@
|
|||
<field name="description" placeholder="Feedback of interviews..."/>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -154,7 +154,7 @@ class hr_analytic_timesheet(osv.osv):
|
|||
'date': lambda self, cr, uid, ctx: ctx.get('date', fields.date.context_today(self,cr,uid,context=ctx)),
|
||||
'user_id': lambda obj, cr, uid, ctx: ctx.get('user_id', uid),
|
||||
}
|
||||
def on_change_account_id(self, cr, uid, ids, account_id):
|
||||
def on_change_account_id(self, cr, uid, ids, account_id, context=None):
|
||||
return {'value':{}}
|
||||
|
||||
def on_change_date(self, cr, uid, ids, date):
|
||||
|
@ -190,17 +190,14 @@ class hr_analytic_timesheet(osv.osv):
|
|||
'journal_id': self._getAnalyticJournal(cr, uid, context),
|
||||
}}
|
||||
|
||||
hr_analytic_timesheet()
|
||||
|
||||
class account_analytic_account(osv.osv):
|
||||
|
||||
_inherit = 'account.analytic.account'
|
||||
_description = 'Analytic Account'
|
||||
|
||||
_columns = {
|
||||
'use_timesheets': fields.boolean('Timesheets', help="Check this field if this project manages timesheets"),
|
||||
'use_timesheets': fields.boolean('Use Timesheets', help="Check this field if this project manages timesheets"),
|
||||
}
|
||||
|
||||
|
||||
def on_change_template(self, cr, uid, ids, template_id, context=None):
|
||||
res = super(account_analytic_account, self).on_change_template(cr, uid, ids, template_id, context=context)
|
||||
if template_id and 'value' in res:
|
||||
|
@ -208,6 +205,4 @@ class account_analytic_account(osv.osv):
|
|||
res['value']['use_timesheets'] = template.use_timesheets
|
||||
return res
|
||||
|
||||
account_analytic_account()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
<field name="name">hr.analytic.timesheet.tree</field>
|
||||
<field name="model">hr.analytic.timesheet</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" string="Timesheet Lines">
|
||||
<tree editable="bottom" string="Timesheet Activities">
|
||||
<field name="date" on_change="on_change_date(date)"/>
|
||||
<field name="user_id" on_change="on_change_user_id(user_id)" required="1"/>
|
||||
<field name="user_id" on_change="on_change_user_id(user_id)" required="1" options='{"no_open": True}'/>
|
||||
<field name="journal_id" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field domain="[('type','=','normal'),('use_timesheets','=',1)]" name="account_id" context="{'default_use_timesheets': 1}"/>
|
||||
<field domain="[('type','=','normal'),('use_timesheets','=',1)]" name="account_id" context="{'default_use_timesheets': 1, 'default_type': 'contract'}"/>
|
||||
<field name="product_id" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" required="1" domain="[('type','=','service')]" invisible="1"/>
|
||||
<field name="unit_amount" string="Duration" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" sum="Total time" widget="float_time"/>
|
||||
<field name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" invisible="1"/>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<field name="name">hr.analytic.timesheet.form</field>
|
||||
<field name="model">hr.analytic.timesheet</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Timesheet Lines" version="7.0">
|
||||
<form string="Timesheet Activities" version="7.0">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
|
@ -85,6 +85,23 @@
|
|||
<field name="src_model">account.analytic.account</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
No activity yet on this contract.
|
||||
</p><p>
|
||||
In OpenERP, contracts and projects are implemented using
|
||||
analytic account. So, you can track costs and revenues to analyse
|
||||
your margins easily.
|
||||
</p><p>
|
||||
Costs will be created automatically when you register supplier
|
||||
invoices, expenses or timesheets.
|
||||
</p><p>
|
||||
Revenues will be created automatically when you create customer
|
||||
invoices. Customer invoices can be created based on sale orders
|
||||
(fixed price invoices), on timesheets (based on the work done) or
|
||||
on expenses (e.g. reinvoicing of travel costs).
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
@ -107,7 +124,7 @@
|
|||
</record>
|
||||
|
||||
<record id="act_hr_timesheet_line_evry1_all_form" model="ir.actions.act_window">
|
||||
<field name="name">Timesheet Lines</field>
|
||||
<field name="name">Timesheet Activities</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">hr.analytic.timesheet</field>
|
||||
<field name="view_type">form</field>
|
||||
|
@ -116,12 +133,12 @@
|
|||
<field name="search_view_id" ref="hr_timesheet_line_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to record your timesheets.
|
||||
Click to record activities.
|
||||
</p><p>
|
||||
You can register and track your workings hours by project every
|
||||
day. Every time spent on a project will become a cost in the
|
||||
analytic accounting and can be re-invoiced to customers if
|
||||
required.
|
||||
analytic accounting/contract and can be re-invoiced to
|
||||
customers if required.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -7,14 +7,14 @@ msgstr ""
|
|||
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
|
||||
"PO-Revision-Date: 2012-05-10 18:19+0000\n"
|
||||
"Last-Translator: Jeff Wang <wjfonhand@hotmail.com>\n"
|
||||
"PO-Revision-Date: 2012-10-25 17:20+0000\n"
|
||||
"Last-Translator: ccdos <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: hr_timesheet
|
||||
#: code:addons/hr_timesheet/report/user_timesheet.py:43
|
||||
|
@ -468,7 +468,7 @@ msgstr "一般信息"
|
|||
#. module: hr_timesheet
|
||||
#: model:ir.actions.act_window,name:hr_timesheet.action_hr_timesheet_my
|
||||
msgid "My Current Timesheet"
|
||||
msgstr ""
|
||||
msgstr "我当前的时间表"
|
||||
|
||||
#. module: hr_timesheet
|
||||
#: code:addons/hr_timesheet/report/user_timesheet.py:40
|
||||
|
|
|
@ -70,8 +70,7 @@ class account_analytic_account(osv.osv):
|
|||
'amount_invoiced': fields.function(_invoiced_calc, string='Invoiced Amount',
|
||||
help="Total invoiced"),
|
||||
'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Timesheet Invoicing Ratio',
|
||||
help="This field allows you to define the rate in case you plan to reinvoice " \
|
||||
"the costs in this analytic account: timesheets, expenses, ..."),
|
||||
help="You usually invoice 100% of the timesheets. But if you mix fixed price and timesheet invoicing, you may use another ratio. For instance, if you do a 20% advance invoice (fixed price, based on a sale order), you should invoice the rest on timesheet with a 80% ratio."),
|
||||
}
|
||||
_defaults = {
|
||||
'pricelist_id': lambda self, cr, uid, ctx: ctx.get('pricelist_id', False),
|
||||
|
|
|
@ -16,5 +16,10 @@
|
|||
<field name="customer_name">50%</field>
|
||||
<field name="factor">50.0</field>
|
||||
</record>
|
||||
<record id="timesheet_invoice_factor4" model="hr_timesheet_invoice.factor">
|
||||
<field name="name">80%</field>
|
||||
<field name="customer_name">80%</field>
|
||||
<field name="factor">20.0</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<xpath expr='//separator[@name="description"]' position='before'>
|
||||
<group string="Invoice on Timesheets Options" name="invoice_on_timesheets" col="4">
|
||||
<field name="pricelist_id" />
|
||||
<field name="to_invoice" widget="selection"/>
|
||||
<field name="pricelist_id" groups="product.group_sale_pricelist"/>
|
||||
<field name="to_invoice"/>
|
||||
</group>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='use_timesheets']" position="replace">
|
||||
|
@ -91,6 +91,20 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_timesheet_line_search_to_invoice" model="ir.ui.view">
|
||||
<field name="name">hr.analytic.timesheet.search.to_invoice</field>
|
||||
<field name="model">hr.analytic.timesheet</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_line_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='account_id']" position="after">
|
||||
<separator/>
|
||||
<filter name="to_invoice" string="To Invoice" context="{'to_invoice': 1}" domain="[('invoice_id','=',False), ('to_invoice','<>',False)]" icon="terp-dolar"/>
|
||||
<filter name="invoiced" string="Invoiced" domain="[('invoice_id','!=',False), ('to_invoice','<>',False)]" icon="terp-dolar"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_account_analytic_line_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">account.analytic.line.tree.to_invoice</field>
|
||||
<field name="model">account.analytic.line</field>
|
||||
|
|
|
@ -36,14 +36,17 @@ class final_invoice_create(osv.osv_memory):
|
|||
_description = 'Create invoice from timesheet final'
|
||||
_columns = {
|
||||
'date': fields.boolean('Date', help='Display date in the history of works'),
|
||||
'time': fields.boolean('Time spent', help='Display time in the history of works'),
|
||||
'name': fields.boolean('Name of entry', help='Display detail of work in the invoice line.'),
|
||||
'time': fields.boolean('Time Spent', help='Display time in the history of works'),
|
||||
'name': fields.boolean('Log of Activity', help='Display detail of work in the invoice line.'),
|
||||
'price': fields.boolean('Cost', help='Display cost of the item you reinvoice'),
|
||||
'product': fields.many2one('product.product', 'Product', help='The product that will be used to invoice the remaining amount'),
|
||||
}
|
||||
|
||||
def do_create(self, cr, uid, ids, context=None):
|
||||
data = self.read(cr, uid, ids, [], context=context)[0]
|
||||
# hack for fixing small issue (context should not propagate implicitly between actions)
|
||||
if 'default_type' in context:
|
||||
del context['default_type']
|
||||
ids = self.pool.get('account.analytic.line').search(cr, uid, [('invoice_id','=',False),('to_invoice','<>', False), ('account_id', 'in', context['active_ids'])], context=context)
|
||||
invs = self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, ids, data, context=context)
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
|
|
|
@ -29,6 +29,15 @@ from tools.translate import _
|
|||
|
||||
class account_analytic_line(osv.osv):
|
||||
_inherit = "account.analytic.line"
|
||||
def _get_invoice_price(self, cr, uid, account, product_id, user_id, qty, context = {}):
|
||||
pro_price_obj = self.pool.get('product.pricelist')
|
||||
if account.pricelist_id:
|
||||
pl = account.pricelist_id.id
|
||||
price = pro_price_obj.price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id, context=context)[pl]
|
||||
else:
|
||||
price = 0.0
|
||||
return price
|
||||
|
||||
|
||||
#
|
||||
# data = {
|
||||
|
@ -45,7 +54,6 @@ class account_analytic_line(osv.osv):
|
|||
invoice_obj = self.pool.get('account.invoice')
|
||||
product_obj = self.pool.get('product.product')
|
||||
invoice_factor_obj = self.pool.get('hr_timesheet_invoice.factor')
|
||||
pro_price_obj = self.pool.get('product.pricelist')
|
||||
fiscal_pos_obj = self.pool.get('account.fiscal.position')
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
invoice_line_obj = self.pool.get('account.invoice.line')
|
||||
|
@ -99,13 +107,13 @@ class account_analytic_line(osv.osv):
|
|||
last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context2)
|
||||
invoices.append(last_invoice)
|
||||
|
||||
cr.execute("SELECT product_id, to_invoice, sum(unit_amount), product_uom_id, name " \
|
||||
cr.execute("SELECT product_id, user_id, to_invoice, sum(unit_amount), product_uom_id, name " \
|
||||
"FROM account_analytic_line as line " \
|
||||
"WHERE account_id = %s " \
|
||||
"AND id IN %s AND to_invoice IS NOT NULL " \
|
||||
"GROUP BY product_id, to_invoice, product_uom_id, name", (account.id, tuple(ids),))
|
||||
"GROUP BY product_id, user_id, to_invoice, product_uom_id, name", (account.id, tuple(ids),))
|
||||
|
||||
for product_id, factor_id, qty, uom, line_name in cr.fetchall():
|
||||
for product_id, user_id, factor_id, qty, uom, line_name in cr.fetchall():
|
||||
if data.get('product'):
|
||||
product_id = data['product'][0]
|
||||
product = product_obj.browse(cr, uid, product_id, context=context2)
|
||||
|
@ -118,11 +126,8 @@ class account_analytic_line(osv.osv):
|
|||
|
||||
ctx = context.copy()
|
||||
ctx.update({'uom':uom})
|
||||
if account.pricelist_id:
|
||||
pl = account.pricelist_id.id
|
||||
price = pro_price_obj.price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id, context=ctx)[pl]
|
||||
else:
|
||||
price = 0.0
|
||||
|
||||
price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, ctx)
|
||||
|
||||
general_account = product.product_tmpl_id.property_account_income or product.categ_id.property_account_income_categ
|
||||
if not general_account:
|
||||
|
@ -164,7 +169,8 @@ class account_analytic_line(osv.osv):
|
|||
details.append(line['name'])
|
||||
note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
|
||||
|
||||
curr_line['name'] += "\n".join(map(lambda x: unicode(x) or '',note))
|
||||
if note:
|
||||
curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note)))
|
||||
invoice_line_obj.create(cr, uid, curr_line, context=context)
|
||||
cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
|
||||
|
||||
|
|
|
@ -26,13 +26,19 @@
|
|||
<field name="model">hr.timesheet.invoice.create.final</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Invoice contract" version="7.0">
|
||||
<separator string="Do you want to display work details on the invoice?" colspan="4"/>
|
||||
<field name="date"/>
|
||||
<field name="time"/>
|
||||
<field name="name"/>
|
||||
<field name="price"/>
|
||||
<separator string="Force to use a special product" colspan="4"/>
|
||||
<field name="product"/>
|
||||
<p>Do you want to show details of each activity to your customer?</p>
|
||||
<group>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
<field name="time"/>
|
||||
</group><group>
|
||||
<field name="name"/>
|
||||
<field name="price"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Force to use a special product" groups="base.group_no_one">
|
||||
<field name="product"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="do_create" string="Create Invoice" type="object" class="oe_highlight"/>
|
||||
or
|
||||
|
|
|
@ -34,103 +34,22 @@ class hr_timesheet_sheet(osv.osv):
|
|||
_order = "id desc"
|
||||
_description="Timesheet"
|
||||
|
||||
def _total_attendances(self, cr, uid, ids, name, args, context=None):
|
||||
""" Get the total attendance for the timesheets
|
||||
Returns a dict like :
|
||||
{id: {'total_per_day': {day: timedelta, ...},
|
||||
},
|
||||
...
|
||||
}
|
||||
"""
|
||||
context = context or {}
|
||||
attendance_obj = self.pool.get('hr.attendance')
|
||||
res = {}
|
||||
for sheet_id in ids:
|
||||
sheet = self.browse(cr, uid, sheet_id, context=context)
|
||||
# field attendances_ids of hr_timesheet_sheet.sheet only
|
||||
# returns attendances of timesheet's current date
|
||||
attendance_ids = attendance_obj.search(cr, uid, [('sheet_id', '=', sheet_id)], context=context)
|
||||
attendances = attendance_obj.browse(cr, uid, attendance_ids, context=context)
|
||||
total_attendance = {}
|
||||
for attendance in [att for att in attendances
|
||||
if att.action in ('sign_in', 'sign_out')]:
|
||||
day = attendance.name[:10]
|
||||
if not total_attendance.get(day, False):
|
||||
total_attendance[day] = timedelta(seconds=0)
|
||||
|
||||
attendance_in_time = datetime.strptime(attendance.name, '%Y-%m-%d %H:%M:%S')
|
||||
attendance_interval = timedelta(hours=attendance_in_time.hour,
|
||||
minutes=attendance_in_time.minute,
|
||||
seconds=attendance_in_time.second)
|
||||
if attendance.action == 'sign_in':
|
||||
total_attendance[day] -= attendance_interval
|
||||
else:
|
||||
total_attendance[day] += attendance_interval
|
||||
|
||||
res[sheet_id] = {'total_per_day': total_attendance}
|
||||
return res
|
||||
|
||||
def _total_timesheet(self, cr, uid, ids, name, args, context=None):
|
||||
""" Get the total of analytic lines for the timesheets
|
||||
Returns a dict like :
|
||||
{id: {day: timedelta, ...}}
|
||||
"""
|
||||
context = context or {}
|
||||
sheet_line_obj = self.pool.get('hr.analytic.timesheet')
|
||||
|
||||
res = {}
|
||||
for sheet_id in ids:
|
||||
# field timesheet_ids of hr_timesheet_sheet.sheet only
|
||||
# returns lines of timesheet's current date
|
||||
sheet_lines_ids = sheet_line_obj.search(cr, uid, [('sheet_id', '=', sheet_id)], context=context)
|
||||
sheet_lines = sheet_line_obj.browse(cr, uid, sheet_lines_ids, context=context)
|
||||
total_timesheet = {}
|
||||
for line in sheet_lines:
|
||||
day = line.date
|
||||
if not total_timesheet.get(day, False):
|
||||
total_timesheet[day] = timedelta(seconds=0)
|
||||
total_timesheet[day] += timedelta(hours=line.unit_amount)
|
||||
res[sheet_id] = total_timesheet
|
||||
return res
|
||||
|
||||
def _total(self, cr, uid, ids, name, args, context=None):
|
||||
""" Compute the attendances, analytic lines timesheets and differences between them
|
||||
for all the days of a timesheet and the current day
|
||||
"""
|
||||
def sum_all_days(sheet_amounts):
|
||||
if not sheet_amounts:
|
||||
return timedelta(seconds=0)
|
||||
total = reduce(lambda memo, value: memo + value, sheet_amounts.values())
|
||||
return total
|
||||
|
||||
def timedelta_to_hours(delta):
|
||||
hours = 0.0
|
||||
seconds = float(delta.seconds)
|
||||
if delta.microseconds:
|
||||
seconds += float(delta.microseconds) / 100000
|
||||
hours += delta.days * 24
|
||||
if seconds:
|
||||
hours += seconds / 3600
|
||||
return hours
|
||||
|
||||
res = {}
|
||||
all_timesheet_attendances = self._total_attendances(cr, uid, ids, name, args, context=context)
|
||||
all_timesheet_lines = self._total_timesheet(cr, uid, ids, name, args, context=context)
|
||||
for id in ids:
|
||||
res[id] = {}
|
||||
|
||||
all_attendances_sheet = all_timesheet_attendances[id]
|
||||
|
||||
total_attendances_sheet = all_attendances_sheet['total_per_day']
|
||||
total_attendances_all_days = sum_all_days(total_attendances_sheet)
|
||||
|
||||
total_timesheets_sheet = all_timesheet_lines[id]
|
||||
total_timesheets_all_days = sum_all_days(total_timesheets_sheet)
|
||||
total_difference_all_days = total_attendances_all_days - total_timesheets_all_days
|
||||
|
||||
res[id]['total_attendance'] = timedelta_to_hours(total_attendances_all_days)
|
||||
res[id]['total_timesheet'] = timedelta_to_hours(total_timesheets_all_days)
|
||||
res[id]['total_difference'] = timedelta_to_hours(total_difference_all_days)
|
||||
for sheet in self.browse(cr, uid, ids, context=context or {}):
|
||||
res.setdefault(sheet.id, {
|
||||
'total_attendance': 0.0,
|
||||
'total_timesheet': 0.0,
|
||||
'total_difference': 0.0,
|
||||
})
|
||||
for period in sheet.period_ids:
|
||||
res[sheet.id]['total_attendance'] += period.total_attendance
|
||||
res[sheet.id]['total_timesheet'] += period.total_timesheet
|
||||
res[sheet.id]['total_difference'] += period.total_attendance - period.total_timesheet
|
||||
return res
|
||||
|
||||
def check_employee_attendance_state(self, cr, uid, sheet_id, context=None):
|
||||
|
@ -160,7 +79,7 @@ class hr_timesheet_sheet(osv.osv):
|
|||
if not new_user_id:
|
||||
raise osv.except_osv(_('Error!'), _('In order to create a timesheet for this employee, you must assign it to a user.'))
|
||||
if not self._sheet_date(cr, uid, ids, forced_user_id=new_user_id):
|
||||
raise osv.except_osv(_('Error!'), _('You cannot have 2 timesheets that overlaps!\nYou should use the menu \'My Timesheet\' to avoid this problem.'))
|
||||
raise osv.except_osv(_('Error!'), _('You cannot have 2 timesheets that overlap!\nYou should use the menu \'My Timesheet\' to avoid this problem.'))
|
||||
if not self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).product_id:
|
||||
raise osv.except_osv(_('Error!'), _('In order to create a timesheet for this employee, you must link the employee to a product.'))
|
||||
if not self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).journal_id:
|
||||
|
@ -268,7 +187,7 @@ class hr_timesheet_sheet(osv.osv):
|
|||
|
||||
|
||||
_constraints = [
|
||||
(_sheet_date, 'You cannot have 2 timesheets that overlaps !\nPlease use the menu \'My Current Timesheet\' to avoid this problem.', ['date_from','date_to']),
|
||||
(_sheet_date, 'You cannot have 2 timesheets that overlap!\nPlease use the menu \'My Current Timesheet\' to avoid this problem.', ['date_from','date_to']),
|
||||
]
|
||||
|
||||
def action_set_to_draft(self, cr, uid, ids, *args):
|
||||
|
@ -283,8 +202,8 @@ class hr_timesheet_sheet(osv.osv):
|
|||
return []
|
||||
if isinstance(ids, (long, int)):
|
||||
ids = [ids]
|
||||
return [(r['id'], r['date_from'] + ' - ' + r['date_to']) \
|
||||
for r in self.read(cr, uid, ids, ['date_from', 'date_to'],
|
||||
return [(r['id'], _('Week ')+datetime.strptime(r['date_from'], '%Y-%m-%d').strftime('%U')) \
|
||||
for r in self.read(cr, uid, ids, ['date_from'],
|
||||
context=context, load='_classic_write')]
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
|
@ -304,16 +223,26 @@ class hr_timesheet_sheet(osv.osv):
|
|||
|
||||
hr_timesheet_sheet()
|
||||
|
||||
|
||||
class hr_timesheet_line(osv.osv):
|
||||
_inherit = "hr.analytic.timesheet"
|
||||
class account_analytic_line(osv.osv):
|
||||
_inherit = "account.analytic.line"
|
||||
|
||||
def _get_default_date(self, cr, uid, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
if 'date' in context:
|
||||
return context['date']
|
||||
return time.strftime('%Y-%m-%d')
|
||||
#get the default date (should be: today)
|
||||
res = super(account_analytic_line, self)._get_default_date(cr, uid, context=context)
|
||||
#if we got the dates from and to from the timesheet and if the default date is in between, we use the default
|
||||
#but if the default isn't included in those dates, we use the date start of the timesheet as default
|
||||
if context.get('timesheet_date_from') and context.get('timesheet_date_to'):
|
||||
if context['timesheet_date_from'] <= res <= context['timesheet_date_to']:
|
||||
return res
|
||||
return context.get('timesheet_date_from')
|
||||
#if we don't get the dates from the timesheet, we return the default value from super()
|
||||
return res
|
||||
|
||||
|
||||
class hr_timesheet_line(osv.osv):
|
||||
_inherit = "hr.analytic.timesheet"
|
||||
|
||||
def _sheet(self, cursor, user, ids, name, args, context=None):
|
||||
sheet_obj = self.pool.get('hr_timesheet_sheet.sheet')
|
||||
|
@ -359,9 +288,6 @@ class hr_timesheet_line(osv.osv):
|
|||
},
|
||||
),
|
||||
}
|
||||
_defaults = {
|
||||
'date': _get_default_date,
|
||||
}
|
||||
|
||||
def _check_sheet_state(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
|
|
|
@ -40,6 +40,23 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="act_hr_timesheet_sheet_sheet_2_hr_analytic_timesheet">
|
||||
<field name="context">{'search_default_sheet_id': [active_id]}</field>
|
||||
<field name="name">Timesheet Activities</field>
|
||||
<field name="res_model">hr.analytic.timesheet</field>
|
||||
<field name="src_model">hr_timesheet_sheet.sheet</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="act_hr_timesheet_sheet_sheet_2_hr_attendance">
|
||||
<field name="context">{'search_default_sheet_id': [active_id]}</field>
|
||||
<field name="name">Attendances</field>
|
||||
<field name="res_model">hr.attendance</field>
|
||||
<field name="src_model">hr_timesheet_sheet.sheet</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="hr_timesheet_sheet_form" model="ir.ui.view">
|
||||
<field name="name">hr.timesheet.sheet.form</field>
|
||||
<field name="model">hr_timesheet_sheet.sheet</field>
|
||||
|
@ -53,8 +70,18 @@
|
|||
<field name="state" widget="statusbar" statusbar_visible="new,confirm,done"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<label for="employee_id" class="oe_edit_only"/>
|
||||
<h1><field name="employee_id" on_change="onchange_employee_id(employee_id)" class="oe_inline"/></h1>
|
||||
<div class="oe_right oe_button_box" name="buttons">
|
||||
<button type="action"
|
||||
name="%(act_hr_timesheet_sheet_sheet_2_hr_analytic_timesheet)d"
|
||||
string="Timesheet Activities" />
|
||||
<button type="action" groups="base.group_hr_attendance"
|
||||
name="%(act_hr_timesheet_sheet_sheet_2_hr_attendance)d"
|
||||
string="Attendances" />
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="employee_id" class="oe_edit_only"/>
|
||||
<h1><field name="employee_id" on_change="onchange_employee_id(employee_id)" class="oe_inline"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<label for="date_from" string="Timesheet Period"/>
|
||||
|
@ -63,41 +90,25 @@
|
|||
<field name="department_id" invisible="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="total_attendance" widget="float_time" groups="base.group_hr_attendance"/>
|
||||
<field name="total_timesheet" widget="float_time" groups="base.group_hr_attendance"/>
|
||||
<field name="total_difference" widget="float_time" groups="base.group_hr_attendance"/>
|
||||
<group groups="base.group_hr_attendance">
|
||||
<field name="total_attendance" widget="float_time"/>
|
||||
<field name="total_timesheet" widget="float_time"/>
|
||||
<field name="total_difference" widget="float_time"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Weekly">
|
||||
<page string="Summary">
|
||||
<widget type="weekly_timesheet">
|
||||
</widget>
|
||||
</page>
|
||||
<page string="Daily">
|
||||
<group groups="base.group_hr_attendance">
|
||||
<field context="{'user_id':user_id}" name="attendances_ids" nolabel="1">
|
||||
<tree string="Attendances" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="action"/>
|
||||
<field invisible="1" name="employee_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
<group>
|
||||
<field name="state_attendance" nolabel="1"/>
|
||||
<div align="right" groups="base.group_hr_manager">
|
||||
<button name="attendance_action_change" attrs="{'invisible': [('state_attendance', '=', 'present')]}" type="object" string="Sign In"/>
|
||||
<button name="attendance_action_change" attrs="{'invisible': ['|', ('state_attendance','=',False), ('state_attendance', '=', 'absent')]}" type="object" string="Sign Out"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<field context="{'user_id':user_id}" name="timesheet_ids" nolabel="1">
|
||||
<tree editable="top" string="Timesheet Lines">
|
||||
<page string="Details">
|
||||
<field context="{'user_id':user_id, 'timesheet_date_from': date_from, 'timesheet_date_to': date_to}" name="timesheet_ids" nolabel="1">
|
||||
<tree editable="top" string="Timesheet Activities">
|
||||
<field name="date"/>
|
||||
<field domain="[('type','in',['normal', 'contract']), ('state', '<>', 'close'),('use_timesheets','=',1)]" name="account_id" on_change="on_change_account_id(account_id, user_id)" context="{'default_use_timesheets': 1}"/>
|
||||
<field name="name"/>
|
||||
<field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" widget="float_time" string="Hours"/>
|
||||
<field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" widget="float_time" string="Hours" sum="Hours"/>
|
||||
<field name="to_invoice" widget="selection"/>
|
||||
<field invisible="1" name="journal_id"/>
|
||||
<field invisible="1" name="product_id" domain="[('type','=','service')]" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)"/>
|
||||
|
@ -106,7 +117,7 @@
|
|||
<field invisible="1" name="general_account_id"/>
|
||||
<field invisible="1" name="user_id" required="1"/>
|
||||
</tree>
|
||||
<form string="Timesheet Lines" version="7.0">
|
||||
<form string="Timesheet Activities" version="7.0">
|
||||
<field name="date"/>
|
||||
<field domain="[('type','=','normal'), ('state', '<>', 'close')]" name="account_id" on_change="on_change_account_id(account_id, user_id)"/>
|
||||
<field name="name"/>
|
||||
|
@ -121,13 +132,30 @@
|
|||
</form>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Summary" groups="base.group_hr_attendance">
|
||||
<page string="Attendances" groups="base.group_hr_attendance">
|
||||
<group>
|
||||
<field context="{'user_id':user_id}" name="attendances_ids" nolabel="1">
|
||||
<tree string="Attendances" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="action"/>
|
||||
<field invisible="1" name="employee_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
<group>
|
||||
<label for="state_attendance"/>
|
||||
<div>
|
||||
<field name="state_attendance"/>
|
||||
<button name="attendance_action_change" attrs="{'invisible': [('state_attendance', '=', 'present')]}" type="object" string="Sign In" class="oe_link"/>
|
||||
<button name="attendance_action_change" attrs="{'invisible': ['|', ('state_attendance','=',False), ('state_attendance', '=', 'absent')]}" type="object" string="Sign Out" class="oe_link"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<field name="period_ids">
|
||||
<tree colors="red:total_difference<0.1;blue:total_difference>=0.1" string="Period">
|
||||
<field name="name"/>
|
||||
<field name="total_attendance" widget="float_time"/>
|
||||
<field name="total_timesheet" widget="float_time"/>
|
||||
<field name="total_difference" widget="float_time"/>
|
||||
<field name="total_attendance" widget="float_time" sum="Attendances"/>
|
||||
<field name="total_timesheet" widget="float_time" sum="Timesheets"/>
|
||||
<field name="total_difference" widget="float_time" sum="Differences"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
|
@ -147,8 +175,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<search string="Search Timesheet">
|
||||
<field name="date_from"/>
|
||||
<filter icon="terp-document-new" string="In Draft" domain="[('state','in',('draft', 'new'))]" help="Unvalidated Timesheets"/>
|
||||
<filter icon="terp-camera_test" string="To Approve" domain="[('state','=','confirm')]" help="Confirmed Timesheets"/>
|
||||
<filter name="new" string="In Draft" domain="[('state','in',('draft', 'new'))]" help="Unvalidated Timesheets"/>
|
||||
<filter name="to_approve" string="To Approve" domain="[('state','=','confirm')]" help="Confirmed Timesheets"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id"/>
|
||||
<group expand="0" string="Group By...">
|
||||
|
@ -160,23 +188,23 @@
|
|||
</record>
|
||||
|
||||
<record id="act_hr_timesheet_sheet_form" model="ir.actions.act_window">
|
||||
<field name="name">Timesheets</field>
|
||||
<field name="name">Timesheets to Validate</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">hr_timesheet_sheet.sheet</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" eval="False"/>
|
||||
<field name="context">{'search_default_my_timesheet':1}</field>
|
||||
<field name="context">{'search_default_to_approve':1}</field>
|
||||
<field name="search_view_id" ref="view_hr_timesheet_sheet_filter"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to go to your timesheet.
|
||||
New timesheet to approve.
|
||||
</p><p>
|
||||
You must record timesheets every day and confirm at the end
|
||||
of the week. Once the timesheet is confirmed, it his sent to
|
||||
the manager for validation.
|
||||
of the week. Once the timesheet is confirmed, it should be
|
||||
validated by a manager.
|
||||
</p><p>
|
||||
Timesheets can also be invoiced to customers, depending on the
|
||||
configuration of the project.
|
||||
configuration of each project's related contract.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -275,33 +303,20 @@
|
|||
</record>
|
||||
|
||||
<act_window
|
||||
context="{'search_default_sheet_id': [active_id]}"
|
||||
id="act_hr_timesheet_sheet_sheet_by_account"
|
||||
name="Timesheet by Account"
|
||||
res_model="hr_timesheet_sheet.sheet.account"
|
||||
src_model="hr_timesheet_sheet.sheet"/>
|
||||
context="{'search_default_sheet_id': [active_id]}"
|
||||
id="act_hr_timesheet_sheet_sheet_by_account"
|
||||
name="Timesheet by Account"
|
||||
groups="base.group_hr_attendance"
|
||||
res_model="hr_timesheet_sheet.sheet.account"
|
||||
src_model="hr_timesheet_sheet.sheet"/>
|
||||
|
||||
<act_window
|
||||
context="{'search_default_sheet_id': [active_id]}"
|
||||
id="act_hr_timesheet_sheet_sheet_by_day"
|
||||
name="Timesheet by Day"
|
||||
res_model="hr_timesheet_sheet.sheet.day"
|
||||
src_model="hr_timesheet_sheet.sheet"/>
|
||||
|
||||
<act_window
|
||||
context="{'search_default_sheet_id': [active_id]}"
|
||||
id="act_hr_timesheet_sheet_sheet_2_hr_analytic_timesheet"
|
||||
name="Timesheet Lines"
|
||||
res_model="hr.analytic.timesheet"
|
||||
src_model="hr_timesheet_sheet.sheet"/>
|
||||
|
||||
|
||||
<act_window
|
||||
context="{'search_default_sheet_id': [active_id]}"
|
||||
id="act_hr_timesheet_sheet_sheet_2_hr_attendance"
|
||||
name="Attendances"
|
||||
res_model="hr.attendance"
|
||||
src_model="hr_timesheet_sheet.sheet"/>
|
||||
context="{'search_default_sheet_id': [active_id]}"
|
||||
id="act_hr_timesheet_sheet_sheet_by_day"
|
||||
name="Timesheet by Day"
|
||||
groups="base.group_hr_attendance"
|
||||
res_model="hr_timesheet_sheet.sheet.day"
|
||||
src_model="hr_timesheet_sheet.sheet"/>
|
||||
|
||||
<record id="hr_timesheet_sheet_tree_simplified" model="ir.ui.view">
|
||||
<field name="name">hr.timesheet.sheet.tree</field>
|
||||
|
@ -312,7 +327,7 @@
|
|||
<field name="employee_id"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="department_id"/>
|
||||
<field name="department_id" invisible="1"/>
|
||||
<field name="total_attendance" widget="float_time" groups="base.group_hr_attendance"/>
|
||||
<field name="total_timesheet" widget="float_time"/>
|
||||
<field name="total_difference" widget="float_time" groups="base.group_hr_attendance"/>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
timesheet.css: timesheet.sass
|
||||
sass -t expanded timesheet.sass timesheet.css
|
||||
|
|
@ -1,79 +1,65 @@
|
|||
|
||||
@charset "utf-8";
|
||||
.openerp .oe_form_readonly .oe_timesheet_weekly .oe_timesheet_button_add {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_form_readonly .oe_timesheet_weekly div.oe_view_nocontent {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_timesheet_weekly {
|
||||
overflow-x: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly table {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly td {
|
||||
padding-top: 15px;
|
||||
padding: 3px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly th {
|
||||
text-align: right;
|
||||
color: #069;
|
||||
font-family: 'Helvetica Neue', Arial, Verdana, 'Nimbus Sans L', sans-serif;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
color: #006699;
|
||||
font-family: "Helvetica Neue", Arial, Verdana, "Nimbus Sans L", sans-serif;
|
||||
font-size: 10px;
|
||||
background: #eeeeee;
|
||||
min-width: 47px;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly th.oe_timesheet_weekly_date_head {
|
||||
width: 60px;
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_total {
|
||||
background: #eeeeee;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly td {
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_weekly_account {
|
||||
text-align: left;
|
||||
padding-right: 30px;
|
||||
text-align: left;
|
||||
}
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_first_col {
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly td input.oe_timesheet_weekly_input {
|
||||
border: 1px solid #CCC;
|
||||
padding: 5px 2px !important;
|
||||
color: #666 !important;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
width: 38px;
|
||||
text-align: right;
|
||||
min-width: 0 !important;
|
||||
padding: 5px 2px !important;
|
||||
width: 40px;
|
||||
text-align: right;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly td .oe_timesheet_weekly_box {
|
||||
padding: 5px 2px !important;
|
||||
color: #666 !important;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
width: 38px;
|
||||
display: inline-block;
|
||||
.openerp .oe_timesheet_weekly td.oe_timesheet_total {
|
||||
font-weight: bold;
|
||||
padding: 5px 3px !important;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_weekly_adding_tot {
|
||||
display: table;
|
||||
width: 100%;
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_weekly_adding {
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_weekly_tottot {
|
||||
display: table-cell;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_weekly_add_row td {
|
||||
text-align: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_weekly_add_row .oe_form_field_many2one {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.openerp .oe_timesheet_weekly_today {
|
||||
.openerp .oe_timesheet_weekly .oe_timesheet_weekly_today {
|
||||
background: #ffddee;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
@charset "utf-8"
|
||||
|
||||
.openerp
|
||||
.oe_form_readonly
|
||||
.oe_timesheet_weekly
|
||||
.oe_timesheet_button_add
|
||||
display: none
|
||||
div.oe_view_nocontent
|
||||
display: none
|
||||
.oe_timesheet_weekly
|
||||
overflow-x: auto
|
||||
table
|
||||
width: 100%
|
||||
td
|
||||
padding: 3px
|
||||
text-align: right
|
||||
th
|
||||
text-align: center
|
||||
color: #069
|
||||
font-family: 'Helvetica Neue', Arial, Verdana, 'Nimbus Sans L', sans-serif
|
||||
font-size: 10px
|
||||
background: #eee
|
||||
min-width: 47px
|
||||
.oe_timesheet_total
|
||||
background: #eee
|
||||
.oe_timesheet_weekly_account
|
||||
text-align: left
|
||||
.oe_timesheet_first_col
|
||||
min-width: 130px
|
||||
td input.oe_timesheet_weekly_input
|
||||
padding: 5px 2px !important
|
||||
width: 40px
|
||||
text-align: right
|
||||
min-width: 0 !important
|
||||
td.oe_timesheet_total
|
||||
font-weight: bold
|
||||
padding: 5px 3px !important
|
||||
.oe_timesheet_weekly_adding_tot
|
||||
display: table
|
||||
width: 100%
|
||||
.oe_timesheet_weekly_adding
|
||||
display: table-cell
|
||||
text-align: left
|
||||
.oe_timesheet_weekly_tottot
|
||||
display: table-cell
|
||||
.oe_timesheet_weekly_add_row td
|
||||
text-align: left
|
||||
.oe_timesheet_weekly_add_row .oe_form_field_many2one
|
||||
display: inline-block
|
||||
width: 200px
|
||||
.oe_timesheet_weekly_today
|
||||
background: #fde
|
|
@ -4,6 +4,9 @@ openerp.hr_timesheet_sheet = function(instance) {
|
|||
var _t = instance.web._t;
|
||||
|
||||
instance.hr_timesheet_sheet.WeeklyTimesheet = instance.web.form.FormWidget.extend(instance.web.form.ReinitializeWidgetMixin, {
|
||||
events: {
|
||||
"click .oe_timesheet_weekly_account a": "go_to",
|
||||
},
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.set({
|
||||
|
@ -26,6 +29,16 @@ openerp.hr_timesheet_sheet = function(instance) {
|
|||
this.render_drop = new instance.web.DropMisordered();
|
||||
this.description_line = _t("/");
|
||||
},
|
||||
go_to: function(event) {
|
||||
var id = JSON.parse($(event.target).data("id"));
|
||||
this.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: "account.analytic.account",
|
||||
res_id: id,
|
||||
views: [[false, 'form']],
|
||||
target: 'current'
|
||||
});
|
||||
},
|
||||
query_sheets: function() {
|
||||
var self = this;
|
||||
if (self.updating)
|
||||
|
@ -194,6 +207,10 @@ openerp.hr_timesheet_sheet = function(instance) {
|
|||
['use_timesheets','=',1],
|
||||
['id', 'not in', _.pluck(self.accounts, "account")],
|
||||
],
|
||||
context: {
|
||||
default_use_timesheets: 1,
|
||||
default_type: "contract",
|
||||
},
|
||||
modifiers: '{"required": true}',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div class="oe_timesheet_weekly">
|
||||
<table>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th class="oe_timesheet_first_col"> </th>
|
||||
<t t-foreach="widget.dates" t-as="date">
|
||||
<th t-att-class="'oe_timesheet_weekly_date_head' + (Date.compare(date, Date.today()) === 0 ? ' oe_timesheet_weekly_today' : '')">
|
||||
<t t-esc="date.toString('ddd')"/><br />
|
||||
|
@ -15,7 +15,7 @@
|
|||
<th class="oe_timesheet_weekly_date_head">Total</th>
|
||||
</tr>
|
||||
<tr t-foreach="widget.accounts" t-as="account">
|
||||
<td class="oe_timesheet_weekly_account"><t t-esc="widget.account_names[account.account]"/></td>
|
||||
<td class="oe_timesheet_weekly_account"><a href="javascript:void(0)" t-att-data-id="JSON.stringify(account.account)"><t t-esc="widget.account_names[account.account]"/></a></td>
|
||||
<t t-set="day_count" t-value="0"/>
|
||||
<t t-foreach="account.days" t-as="day">
|
||||
<td t-att-class="(Date.compare(day.day, Date.today()) === 0 ? 'oe_timesheet_weekly_today' : '')">
|
||||
|
@ -26,31 +26,47 @@
|
|||
<t t-set="day_count" t-value="day_count + 1"/>
|
||||
</td>
|
||||
</t>
|
||||
<td t-att-data-account-total="account.account"> </td>
|
||||
<td t-att-data-account-total="account.account" class="oe_timesheet_total"> </td>
|
||||
</tr>
|
||||
<tr class="oe_timesheet_weekly_add_row" style="display: none">
|
||||
<td t-att-colspan="widget.dates.length + 2">
|
||||
<button>Add</button>
|
||||
<button class="oe_highlight">Add</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr class="oe_timesheet_total">
|
||||
<td>
|
||||
<div class="oe_timesheet_weekly_adding_tot">
|
||||
<div class="oe_timesheet_weekly_adding"><button>Add a Project</button></div>
|
||||
<div class="oe_timesheet_weekly_adding"><button class="oe_timesheet_button_add">Add a Line</button></div>
|
||||
<div class="oe_timesheet_weekly_tottot"><span>Total</span></div>
|
||||
</div>
|
||||
</td>
|
||||
<t t-set="day_count" t-value="0"/>
|
||||
<t t-foreach="widget.dates" t-as="date">
|
||||
<td t-att-class="(Date.compare(date, Date.today()) === 0 ? 'oe_timesheet_weekly_today' : '')">
|
||||
<td class="oe_timesheet_total">
|
||||
<span class="oe_timesheet_weekly_box" t-att-data-day-total="day_count">
|
||||
</span>
|
||||
<t t-set="day_count" t-value="day_count + 1"/>
|
||||
</td>
|
||||
</t>
|
||||
<td class="oe_timesheet_weekly_supertotal"> </td>
|
||||
<td class="oe_timesheet_weekly_supertotal oe_timesheet_total"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<div t-if="widget.accounts.length == 0">
|
||||
<div class="oe_view_nocontent">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add projects/analytic accounts you worked on.
|
||||
</p><p>
|
||||
You will be able to register your working hours and
|
||||
activities.
|
||||
</p><p>
|
||||
By default, you record timesheets on analytic accounts.
|
||||
But if an analytic account represents a customer
|
||||
contract, you can change the type of the analytic
|
||||
account to 'Contract or Project' to setup the invoicing
|
||||
options.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
|
@ -36,7 +36,7 @@ class hr_timesheet_current_open(osv.osv_memory):
|
|||
user_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)], context=context)
|
||||
if not len(user_ids):
|
||||
raise osv.except_osv(_('Error!'), _('Please create an employee and associate it with this user.'))
|
||||
ids = ts.search(cr, uid, [('user_id','=',uid),('state','=','draft'),('date_from','<=',time.strftime('%Y-%m-%d')), ('date_to','>=',time.strftime('%Y-%m-%d'))], context=context)
|
||||
ids = ts.search(cr, uid, [('user_id','=',uid),('state','in',('draft','new')),('date_from','<=',time.strftime('%Y-%m-%d')), ('date_to','>=',time.strftime('%Y-%m-%d'))], context=context)
|
||||
|
||||
if len(ids) > 1:
|
||||
view_type = 'tree,form'
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<field name="name">hr_timesheet_current_open.form</field>
|
||||
<field name="model">hr.timesheet.current.open</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="My Current Timesheet" version="7.0">
|
||||
<form string="My Timesheet" version="7.0">
|
||||
<group>
|
||||
<separator string="It will open your current timesheet"/>
|
||||
</group>
|
||||
|
@ -19,7 +19,7 @@
|
|||
</record>
|
||||
|
||||
<record id="action_hr_timesheet_current_open" model="ir.actions.act_window">
|
||||
<field name="name">My Current Timesheet</field>
|
||||
<field name="name">My Timesheet</field>
|
||||
<field name="res_model">hr.timesheet.current.open</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
|
@ -28,7 +28,7 @@
|
|||
<field name="help">My Timesheet opens your timesheet so that you can book your activities into the system. From the same form, you can register your attendances (Sign In/Out) and describe the working hours made on the different projects. At the end of the period defined in the company, the timesheet is confirmed by the user and can be validated by his manager. If required, as defined on the project, you can generate the invoices based on the timesheet.</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_hr_timesheet_current_open" icon="STOCK_NEW" id="menu_act_hr_timesheet_sheet_form_my_current" name="My Current Timesheet" parent="hr_attendance.menu_hr_time_tracking" sequence="1"/>
|
||||
<menuitem action="action_hr_timesheet_current_open" icon="STOCK_NEW" id="menu_act_hr_timesheet_sheet_form_my_current" name="My Timesheet" parent="hr_attendance.menu_hr_time_tracking" sequence="1"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -79,8 +79,8 @@
|
|||
<field name="description"/>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -84,7 +84,6 @@ Main Features
|
|||
'css': [
|
||||
'static/src/css/mail.css',
|
||||
'static/src/css/mail_group.css',
|
||||
'static/src/css/mail_compose_message.css',
|
||||
],
|
||||
'js': [
|
||||
'static/lib/jquery.expander/jquery.expander.js',
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<field name="body">Your monthly meal vouchers arrived. You can get them at Christine's office.
|
||||
This month you also get 250 EUR of eco-vouchers if you have been in the company for more than a year.</field>
|
||||
<field name="type">comment</field>
|
||||
<field name="subtype_id" ref="mt_comment"/>
|
||||
</record>
|
||||
|
||||
<record id="message_blogpost0_comment0" model="mail.message">
|
||||
|
@ -16,6 +17,7 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
|
|||
<field name="body"><![CDATA[Great.]]></field>
|
||||
<field name="parent_id" ref="message_blogpost0"/>
|
||||
<field name="type">comment</field>
|
||||
<field name="subtype_id" ref="mt_comment"/>
|
||||
</record>
|
||||
|
||||
<record id="message_blogpost0_comment1" model="mail.message">
|
||||
|
@ -24,22 +26,25 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
|
|||
<field name="body">Thanks, but where is Christine's office, if I may ask? (I'm new here)</field>
|
||||
<field name="parent_id" ref="message_blogpost0"/>
|
||||
<field name="type">comment</field>
|
||||
<field name="subtype_id" ref="mt_comment"/>
|
||||
</record>
|
||||
|
||||
<record id="message_blogpost0_comment2" model="mail.message">
|
||||
<field name="model">mail.group</field>
|
||||
<field name="res_id" ref="group_all_employees"/>
|
||||
<field name="body">Building B3, second floor on the right :-)</field>
|
||||
<field name="parent_id" ref="message_blogpost0"/>
|
||||
<field name="type">comment</field>
|
||||
<field name="subtype_id" ref="mt_comment"/>
|
||||
</record>
|
||||
|
||||
<record id="message_blogpost0_comment3" model="mail.message">
|
||||
<field name="model">mail.group</field>
|
||||
<field name="res_id" ref="group_all_employees"/>
|
||||
<field name="body">Great news, I need to buy a new fridge, I think I can pay it with the eco-vouchers!</field>
|
||||
<field name="parent_id" ref="message_blogpost0"/>
|
||||
<field name="type">comment</field>
|
||||
</record>
|
||||
|
||||
<record id="message_blogpost0_comment1_2" model="mail.message">
|
||||
<field name="model">mail.group</field>
|
||||
<field name="res_id" ref="group_all_employees"/>
|
||||
<field name="body">Building B3, second floor on the right :-)</field>
|
||||
<field name="parent_id" ref="message_blogpost0_comment1"/>
|
||||
<field name="type">comment</field>
|
||||
<field name="subtype_id" ref="mt_comment"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
|
|
@ -8,45 +8,45 @@ msgstr ""
|
|||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-09 00:36+0000\n"
|
||||
"PO-Revision-Date: 2011-12-22 17:13+0000\n"
|
||||
"Last-Translator: Tomislav Bosnjakovic <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-10-28 19:42+0000\n"
|
||||
"Last-Translator: Marijan Rajic <Unknown>\n"
|
||||
"Language-Team: Croatian <hr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-29 05:00+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,subtype:0 field:mail.message,subtype:0
|
||||
#: field:mail.message.common,subtype:0
|
||||
msgid "Message Type"
|
||||
msgstr ""
|
||||
msgstr "Tip poruke"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,auto_delete:0
|
||||
msgid "Permanently delete emails after sending"
|
||||
msgstr ""
|
||||
msgstr "Trajno obrisati e-poštu nakon slanja"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Open Related Document"
|
||||
msgstr ""
|
||||
msgstr "Otvori povezani dokument"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Open Attachments"
|
||||
msgstr "Otvori privitke"
|
||||
msgstr "Otvori priloge"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Message Details"
|
||||
msgstr "Detalji poruke"
|
||||
msgstr "Detalji porukue"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.thread:0
|
||||
msgid "Communication History"
|
||||
msgstr ""
|
||||
msgstr "Povijest komunikacije"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
|
@ -57,73 +57,73 @@ msgstr "Grupiraj po..."
|
|||
#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Compose Email"
|
||||
msgstr ""
|
||||
msgstr "Sastavi e-poštu"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,body_text:0 help:mail.message,body_text:0
|
||||
#: help:mail.message.common,body_text:0
|
||||
msgid "Plain-text version of the message"
|
||||
msgstr ""
|
||||
msgstr "Verzija poruke u običnom tekstu"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Body"
|
||||
msgstr ""
|
||||
msgstr "Sadržaj"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_to:0 help:mail.message,email_to:0
|
||||
#: help:mail.message.common,email_to:0
|
||||
msgid "Message recipients"
|
||||
msgstr ""
|
||||
msgstr "Primatelji poruke"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,body_text:0 field:mail.message,body_text:0
|
||||
#: field:mail.message.common,body_text:0
|
||||
msgid "Text Contents"
|
||||
msgstr ""
|
||||
msgstr "Sadržaj"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Received"
|
||||
msgstr ""
|
||||
msgstr "Primljeno"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
msgstr "Nit"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.message,mail_server_id:0
|
||||
msgid "Outgoing mail server"
|
||||
msgstr ""
|
||||
msgstr "Izlazni poslužitelj e-pošte"
|
||||
|
||||
#. module: mail
|
||||
#: selection:mail.message,state:0
|
||||
msgid "Cancelled"
|
||||
msgstr ""
|
||||
msgstr "Otkazano"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,reply_to:0 field:mail.message,reply_to:0
|
||||
#: field:mail.message.common,reply_to:0
|
||||
msgid "Reply-To"
|
||||
msgstr ""
|
||||
msgstr "Odgovori na"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,body_html:0 help:mail.message,body_html:0
|
||||
#: help:mail.message.common,body_html:0
|
||||
msgid "Rich-text/HTML version of the message"
|
||||
msgstr ""
|
||||
msgstr "Verzija poruke u Rich-text/HTML formatu"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,auto_delete:0 field:mail.message,auto_delete:0
|
||||
msgid "Auto Delete"
|
||||
msgstr ""
|
||||
msgstr "Auto brisanje"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_bcc:0 help:mail.message,email_bcc:0
|
||||
#: help:mail.message.common,email_bcc:0
|
||||
msgid "Blind carbon copy message recipients"
|
||||
msgstr ""
|
||||
msgstr "Primatelji skrivene kopije poruke"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_res_partner view:mail.message:0
|
||||
|
@ -134,13 +134,13 @@ msgstr "Partner"
|
|||
#: field:mail.compose.message,subject:0 field:mail.message,subject:0
|
||||
#: field:mail.message.common,subject:0
|
||||
msgid "Subject"
|
||||
msgstr ""
|
||||
msgstr "Naslov"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:152
|
||||
#, python-format
|
||||
msgid "On %(date)s, "
|
||||
msgstr ""
|
||||
msgstr "Na %(date)s, "
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,email_from:0 field:mail.message,email_from:0
|
||||
|
@ -151,32 +151,32 @@ msgstr "Od"
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Email message"
|
||||
msgstr ""
|
||||
msgstr "Poruka e-pošte"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Send"
|
||||
msgstr ""
|
||||
msgstr "Pošalji"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Failed"
|
||||
msgstr ""
|
||||
msgstr "Neuspjelo"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 field:mail.message,state:0
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
msgstr "Stanje"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Reply"
|
||||
msgstr ""
|
||||
msgstr "Odgovor"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Sent"
|
||||
msgstr ""
|
||||
msgstr "Poslano"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,subtype:0 help:mail.message,subtype:0
|
||||
|
@ -185,39 +185,41 @@ msgid ""
|
|||
"Type of message, usually 'html' or 'plain', used to select plaintext or rich "
|
||||
"text contents accordingly"
|
||||
msgstr ""
|
||||
"Tip poruke, obično 'html' ili 'običan tekst', koristi se za odabir običnog "
|
||||
"ili bogatije formatiranog teksta"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Recipients"
|
||||
msgstr ""
|
||||
msgstr "Primatelji"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_compose_message
|
||||
msgid "Email composition wizard"
|
||||
msgstr ""
|
||||
msgstr "Čarobnjak za sastavljanje e-pošte"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,res_id:0 field:mail.message,res_id:0
|
||||
#: field:mail.message.common,res_id:0
|
||||
msgid "Related Document ID"
|
||||
msgstr ""
|
||||
msgstr "Povezani ID dokumenta"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
msgstr "Napredno"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:157
|
||||
#, python-format
|
||||
msgid "Re:"
|
||||
msgstr ""
|
||||
msgstr "Re:"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,model:0 field:mail.message,model:0
|
||||
#: field:mail.message.common,model:0
|
||||
msgid "Related Document Model"
|
||||
msgstr ""
|
||||
msgstr "Povezani model dokumenta"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
|
@ -227,42 +229,42 @@ msgstr "Mjesec"
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Email Search"
|
||||
msgstr "Pretraživanje e-mailova"
|
||||
msgstr "Pretraga e-pošte"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.message,original:0
|
||||
msgid "Original version of the message, as it was sent on the network"
|
||||
msgstr ""
|
||||
msgstr "Originalna varijanta poruke, kakva je poslana na mrežu"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Partner Name"
|
||||
msgstr "Naziv partnera"
|
||||
msgstr "Ime partnera"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Retry"
|
||||
msgstr ""
|
||||
msgstr "Pokušaj ponovo"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Outgoing"
|
||||
msgstr ""
|
||||
msgstr "Odlazno"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Send Now"
|
||||
msgstr ""
|
||||
msgstr "Pošalji odmah"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.message,partner_id:0
|
||||
msgid "Related partner"
|
||||
msgstr ""
|
||||
msgstr "Povezani partner"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
msgstr "Korisnik"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,date:0 field:mail.message,date:0
|
||||
|
@ -273,19 +275,19 @@ msgstr "Datum"
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Extended Filters..."
|
||||
msgstr ""
|
||||
msgstr "Prošireni filteri..."
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:153
|
||||
#, python-format
|
||||
msgid "%(sender_name)s wrote:"
|
||||
msgstr ""
|
||||
msgstr "%(sender_name)s je napisao:"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,body_html:0 field:mail.message,body_html:0
|
||||
#: field:mail.message.common,body_html:0
|
||||
msgid "Rich-text Contents"
|
||||
msgstr ""
|
||||
msgstr "Formatirani tekst"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.message,original:0
|
||||
|
@ -346,18 +348,18 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
msgstr "Otvori"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_thread.py:434
|
||||
#, python-format
|
||||
msgid "[OpenERP-Forward-Failed] %s"
|
||||
msgstr ""
|
||||
msgstr "[OpenERP-Forward-Failed] %s"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.message,user_id:0
|
||||
msgid "Related User"
|
||||
msgstr ""
|
||||
msgstr "Povezani korisnik"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,headers:0 help:mail.message,headers:0
|
||||
|
@ -366,11 +368,13 @@ msgid ""
|
|||
"Full message headers, e.g. SMTP session headers (usually available on "
|
||||
"inbound messages only)"
|
||||
msgstr ""
|
||||
"Cjelovito zaglavlje poruke, npr. zaglavlje SMTP sesije (obično dostupno samo "
|
||||
"kod dolaznih poruka)"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Creation Month"
|
||||
msgstr ""
|
||||
msgstr "Mjesec kreiranja"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,email_to:0 field:mail.message,email_to:0
|
||||
|
@ -387,7 +391,7 @@ msgstr "Detalji"
|
|||
#: model:ir.actions.act_window,name:mail.action_view_mailgate_thread
|
||||
#: view:mail.thread:0
|
||||
msgid "Email Threads"
|
||||
msgstr ""
|
||||
msgstr "Niti e-pošte"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_from:0 help:mail.message,email_from:0
|
||||
|
@ -396,28 +400,30 @@ msgid ""
|
|||
"Message sender, taken from user preferences. If empty, this is not a mail "
|
||||
"but a message."
|
||||
msgstr ""
|
||||
"Pošiljatelj poruke, preuzet iz korisničkih preferenci. Ukoliko je prazno, "
|
||||
"biti će poruka a ne e-pošta."
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Body (Plain)"
|
||||
msgstr ""
|
||||
msgstr "Tijelo (obično)"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:153
|
||||
#, python-format
|
||||
msgid "You"
|
||||
msgstr ""
|
||||
msgstr "Vi"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,message_id:0 help:mail.message,message_id:0
|
||||
#: help:mail.message.common,message_id:0
|
||||
msgid "Message unique identifier"
|
||||
msgstr ""
|
||||
msgstr "Jedinstveni identifikator poruke"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Body (Rich)"
|
||||
msgstr ""
|
||||
msgstr "Tijelo (formatirano)"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_message.py:155
|
||||
|
@ -427,6 +433,9 @@ msgid ""
|
|||
" Subject: %s \n"
|
||||
"\t"
|
||||
msgstr ""
|
||||
"%s napisano %s: \n"
|
||||
" Predmet: %s \n"
|
||||
"\t"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.actions.act_window,name:mail.act_res_partner_emails
|
||||
|
@ -445,64 +454,64 @@ msgstr "Poruke"
|
|||
#: field:mail.compose.message,headers:0 field:mail.message,headers:0
|
||||
#: field:mail.message.common,headers:0
|
||||
msgid "Message Headers"
|
||||
msgstr ""
|
||||
msgstr "Zaglavlja poruke"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,email_bcc:0 field:mail.message,email_bcc:0
|
||||
#: field:mail.message.common,email_bcc:0
|
||||
msgid "Bcc"
|
||||
msgstr "Bcc"
|
||||
msgstr "Skrivena kopija"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_message_common
|
||||
msgid "mail.message.common"
|
||||
msgstr ""
|
||||
msgstr "mail.message.common"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,references:0 help:mail.message,references:0
|
||||
#: help:mail.message.common,references:0
|
||||
msgid "Message references, such as identifiers of previous messages"
|
||||
msgstr ""
|
||||
msgstr "Reference poruke, poput identifikatora prethodnih poruka"
|
||||
|
||||
#. module: mail
|
||||
#: constraint:res.partner:0
|
||||
msgid "Error ! You cannot create recursive associated members."
|
||||
msgstr ""
|
||||
msgstr "Greška! Nije moguće kreirati pridružene članove rekurzivno."
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_cc:0 help:mail.message,email_cc:0
|
||||
#: help:mail.message.common,email_cc:0
|
||||
msgid "Carbon copy message recipients"
|
||||
msgstr ""
|
||||
msgstr "Primatelji skrivene kopije"
|
||||
|
||||
#. module: mail
|
||||
#: selection:mail.message,state:0
|
||||
msgid "Delivery Failed"
|
||||
msgstr ""
|
||||
msgstr "Isporuka nije uspjela"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_message
|
||||
msgid "Email Message"
|
||||
msgstr ""
|
||||
msgstr "Poruka e-pošte"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_thread view:mail.thread:0
|
||||
msgid "Email Thread"
|
||||
msgstr ""
|
||||
msgstr "Nit e-pošte"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,filter_id:0
|
||||
msgid "Filters"
|
||||
msgstr ""
|
||||
msgstr "Filteri"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_thread.py:220
|
||||
#, python-format
|
||||
msgid "Mail attachment"
|
||||
msgstr ""
|
||||
msgstr "Prilog e-pošte"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,reply_to:0 help:mail.message,reply_to:0
|
||||
#: help:mail.message.common,reply_to:0
|
||||
msgid "Preferred response address for the message"
|
||||
msgstr ""
|
||||
msgstr "Željena povratna adresa za poruku"
|
||||
|
|
|
@ -7,21 +7,20 @@ msgstr ""
|
|||
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-02-09 00:36+0000\n"
|
||||
"PO-Revision-Date: 2011-01-21 12:01+0000\n"
|
||||
"Last-Translator: NOVOTRADE RENDSZERHÁZ ( novotrade.hu ) "
|
||||
"<openerp@novotrade.hu>\n"
|
||||
"PO-Revision-Date: 2012-10-26 12:29+0000\n"
|
||||
"Last-Translator: Herczeg Péter <herczegp@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-27 04:59+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,subtype:0 field:mail.message,subtype:0
|
||||
#: field:mail.message.common,subtype:0
|
||||
msgid "Message Type"
|
||||
msgstr ""
|
||||
msgstr "Üzenettípus"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,auto_delete:0
|
||||
|
@ -31,7 +30,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Open Related Document"
|
||||
msgstr ""
|
||||
msgstr "Kapcsolódó dokumentum megnyitása"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
|
@ -57,7 +56,7 @@ msgstr "Csoportosítás..."
|
|||
#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Compose Email"
|
||||
msgstr ""
|
||||
msgstr "Email írás"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,body_text:0 help:mail.message,body_text:0
|
||||
|
@ -68,7 +67,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Body"
|
||||
msgstr ""
|
||||
msgstr "Levéltörzs"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_to:0 help:mail.message,email_to:0
|
||||
|
@ -85,7 +84,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Received"
|
||||
msgstr ""
|
||||
msgstr "Fogadott"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
|
@ -95,18 +94,18 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: field:mail.message,mail_server_id:0
|
||||
msgid "Outgoing mail server"
|
||||
msgstr ""
|
||||
msgstr "Kimenő levelező szerver"
|
||||
|
||||
#. module: mail
|
||||
#: selection:mail.message,state:0
|
||||
msgid "Cancelled"
|
||||
msgstr ""
|
||||
msgstr "Megszakítva"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,reply_to:0 field:mail.message,reply_to:0
|
||||
#: field:mail.message.common,reply_to:0
|
||||
msgid "Reply-To"
|
||||
msgstr ""
|
||||
msgstr "Válaszcím"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,body_html:0 help:mail.message,body_html:0
|
||||
|
@ -117,7 +116,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: field:mail.compose.message,auto_delete:0 field:mail.message,auto_delete:0
|
||||
msgid "Auto Delete"
|
||||
msgstr ""
|
||||
msgstr "Automatikus törlés"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_bcc:0 help:mail.message,email_bcc:0
|
||||
|
@ -146,37 +145,37 @@ msgstr ""
|
|||
#: field:mail.compose.message,email_from:0 field:mail.message,email_from:0
|
||||
#: field:mail.message.common,email_from:0
|
||||
msgid "From"
|
||||
msgstr ""
|
||||
msgstr "Feladó"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Email message"
|
||||
msgstr ""
|
||||
msgstr "Email üzenet"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Send"
|
||||
msgstr ""
|
||||
msgstr "Küldés"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Failed"
|
||||
msgstr ""
|
||||
msgstr "Sikertelen"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 field:mail.message,state:0
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
msgstr "Állapot"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Reply"
|
||||
msgstr ""
|
||||
msgstr "Válasz"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Sent"
|
||||
msgstr ""
|
||||
msgstr "Elküldött"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,subtype:0 help:mail.message,subtype:0
|
||||
|
@ -189,7 +188,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Recipients"
|
||||
msgstr ""
|
||||
msgstr "Címzettek"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_compose_message
|
||||
|
@ -205,7 +204,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
msgstr "Speciális"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:157
|
||||
|
@ -242,17 +241,17 @@ msgstr "Partner neve"
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Retry"
|
||||
msgstr ""
|
||||
msgstr "Újra"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Outgoing"
|
||||
msgstr ""
|
||||
msgstr "Kimenő"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Send Now"
|
||||
msgstr ""
|
||||
msgstr "Küldés most"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.message,partner_id:0
|
||||
|
@ -262,7 +261,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
msgstr "Felhasználó"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,date:0 field:mail.message,date:0
|
||||
|
@ -273,7 +272,7 @@ msgstr "Dátum"
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Extended Filters..."
|
||||
msgstr ""
|
||||
msgstr "Kiterjesztett szűrők…"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:153
|
||||
|
@ -290,7 +289,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: field:mail.message,original:0
|
||||
msgid "Original"
|
||||
msgstr ""
|
||||
msgstr "Eredeti"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_thread.py:247 view:res.partner:0
|
||||
|
@ -341,12 +340,12 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.compose.message:0 view:mail.message:0
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
msgstr "Mégsem"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
msgstr "Megnyitás"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_thread.py:434
|
||||
|
@ -357,7 +356,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: field:mail.message,user_id:0
|
||||
msgid "Related User"
|
||||
msgstr ""
|
||||
msgstr "Kapcsolódó felhasználó"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,headers:0 help:mail.message,headers:0
|
||||
|
|
|
@ -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-09 00:36+0000\n"
|
||||
"PO-Revision-Date: 2011-01-25 08:25+0000\n"
|
||||
"Last-Translator: Alexey Y. Fedotov <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-10-26 17:02+0000\n"
|
||||
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
|
||||
"Language-Team: Russian <ru@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-27 04:59+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,subtype:0 field:mail.message,subtype:0
|
||||
|
@ -26,12 +26,12 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: help:mail.compose.message,auto_delete:0
|
||||
msgid "Permanently delete emails after sending"
|
||||
msgstr ""
|
||||
msgstr "Не сохранять сообщения после отправки"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Open Related Document"
|
||||
msgstr ""
|
||||
msgstr "Открыть связанный документ"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
|
@ -46,7 +46,7 @@ msgstr "Подробности сообщения"
|
|||
#. module: mail
|
||||
#: view:mail.thread:0
|
||||
msgid "Communication History"
|
||||
msgstr ""
|
||||
msgstr "История общения"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
|
@ -57,24 +57,24 @@ msgstr "Объеденить по..."
|
|||
#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Compose Email"
|
||||
msgstr ""
|
||||
msgstr "Написать письмо"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,body_text:0 help:mail.message,body_text:0
|
||||
#: help:mail.message.common,body_text:0
|
||||
msgid "Plain-text version of the message"
|
||||
msgstr ""
|
||||
msgstr "Простая текстовая версия сообщения"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Body"
|
||||
msgstr ""
|
||||
msgstr "Содержимое"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_to:0 help:mail.message,email_to:0
|
||||
#: help:mail.message.common,email_to:0
|
||||
msgid "Message recipients"
|
||||
msgstr ""
|
||||
msgstr "Получатели сообщения"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,body_text:0 field:mail.message,body_text:0
|
||||
|
@ -85,7 +85,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Received"
|
||||
msgstr ""
|
||||
msgstr "Получено"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
|
@ -95,40 +95,40 @@ msgstr "Цепочка"
|
|||
#. module: mail
|
||||
#: field:mail.message,mail_server_id:0
|
||||
msgid "Outgoing mail server"
|
||||
msgstr ""
|
||||
msgstr "Сервер исходящей почты"
|
||||
|
||||
#. module: mail
|
||||
#: selection:mail.message,state:0
|
||||
msgid "Cancelled"
|
||||
msgstr ""
|
||||
msgstr "Отменено"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,reply_to:0 field:mail.message,reply_to:0
|
||||
#: field:mail.message.common,reply_to:0
|
||||
msgid "Reply-To"
|
||||
msgstr ""
|
||||
msgstr "Адрес ответа"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,body_html:0 help:mail.message,body_html:0
|
||||
#: help:mail.message.common,body_html:0
|
||||
msgid "Rich-text/HTML version of the message"
|
||||
msgstr ""
|
||||
msgstr "Форматный текст/HTML версия сообщения"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,auto_delete:0 field:mail.message,auto_delete:0
|
||||
msgid "Auto Delete"
|
||||
msgstr ""
|
||||
msgstr "Авто удаление"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_bcc:0 help:mail.message,email_bcc:0
|
||||
#: help:mail.message.common,email_bcc:0
|
||||
msgid "Blind carbon copy message recipients"
|
||||
msgstr ""
|
||||
msgstr "Получатели скрытой копии сообщения"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_res_partner view:mail.message:0
|
||||
msgid "Partner"
|
||||
msgstr "Контрагент"
|
||||
msgstr "Партнёр"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,subject:0 field:mail.message,subject:0
|
||||
|
@ -140,7 +140,7 @@ msgstr "Тема"
|
|||
#: code:addons/mail/wizard/mail_compose_message.py:152
|
||||
#, python-format
|
||||
msgid "On %(date)s, "
|
||||
msgstr ""
|
||||
msgstr "За %(date)s, "
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,email_from:0 field:mail.message,email_from:0
|
||||
|
@ -151,32 +151,32 @@ msgstr "От"
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Email message"
|
||||
msgstr ""
|
||||
msgstr "Сообщение эл. почты"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.compose.message:0
|
||||
msgid "Send"
|
||||
msgstr ""
|
||||
msgstr "Отправить"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Failed"
|
||||
msgstr ""
|
||||
msgstr "Ошибка"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 field:mail.message,state:0
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
msgstr "Состояние"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Reply"
|
||||
msgstr ""
|
||||
msgstr "Ответить"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Sent"
|
||||
msgstr ""
|
||||
msgstr "Отправлено"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,subtype:0 help:mail.message,subtype:0
|
||||
|
@ -185,11 +185,13 @@ msgid ""
|
|||
"Type of message, usually 'html' or 'plain', used to select plaintext or rich "
|
||||
"text contents accordingly"
|
||||
msgstr ""
|
||||
"Тип сообщения, обычно 'html' или 'простой', используемый для выбора "
|
||||
"содержимого соответственно простого текста или форматного текста"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Recipients"
|
||||
msgstr ""
|
||||
msgstr "Получатели"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_compose_message
|
||||
|
@ -200,18 +202,18 @@ msgstr ""
|
|||
#: field:mail.compose.message,res_id:0 field:mail.message,res_id:0
|
||||
#: field:mail.message.common,res_id:0
|
||||
msgid "Related Document ID"
|
||||
msgstr ""
|
||||
msgstr "ID связанного документа"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
msgstr "Дополнительно"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:157
|
||||
#, python-format
|
||||
msgid "Re:"
|
||||
msgstr ""
|
||||
msgstr "Ответ:"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,model:0 field:mail.message,model:0
|
||||
|
@ -232,37 +234,37 @@ msgstr "Искать эл. почту"
|
|||
#. module: mail
|
||||
#: help:mail.message,original:0
|
||||
msgid "Original version of the message, as it was sent on the network"
|
||||
msgstr ""
|
||||
msgstr "Оригинальная версия сообщения, как было отправлено в сеть"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Partner Name"
|
||||
msgstr "Имя партнера"
|
||||
msgstr "Название партнера"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Retry"
|
||||
msgstr ""
|
||||
msgstr "Повторить"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0 selection:mail.message,state:0
|
||||
msgid "Outgoing"
|
||||
msgstr ""
|
||||
msgstr "Исходящее"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Send Now"
|
||||
msgstr ""
|
||||
msgstr "Отправить сейчас"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.message,partner_id:0
|
||||
msgid "Related partner"
|
||||
msgstr ""
|
||||
msgstr "Связанный партнер"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
msgstr "Пользователь"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,date:0 field:mail.message,date:0
|
||||
|
@ -273,13 +275,13 @@ msgstr "Дата"
|
|||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Extended Filters..."
|
||||
msgstr ""
|
||||
msgstr "Расширенные фильтры..."
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:153
|
||||
#, python-format
|
||||
msgid "%(sender_name)s wrote:"
|
||||
msgstr ""
|
||||
msgstr "%(sender_name)s написал:"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,body_html:0 field:mail.message,body_html:0
|
||||
|
@ -290,7 +292,7 @@ msgstr ""
|
|||
#. module: mail
|
||||
#: field:mail.message,original:0
|
||||
msgid "Original"
|
||||
msgstr ""
|
||||
msgstr "Оригинал"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_thread.py:247 view:res.partner:0
|
||||
|
@ -302,13 +304,13 @@ msgstr "История"
|
|||
#: field:mail.compose.message,message_id:0 field:mail.message,message_id:0
|
||||
#: field:mail.message.common,message_id:0
|
||||
msgid "Message-Id"
|
||||
msgstr ""
|
||||
msgstr "ID сообщения"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.compose.message:0 field:mail.compose.message,attachment_ids:0
|
||||
#: view:mail.message:0 field:mail.message,attachment_ids:0
|
||||
msgid "Attachments"
|
||||
msgstr "Прикрепленные файлы"
|
||||
msgstr "Вложения"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,email_cc:0 field:mail.message,email_cc:0
|
||||
|
@ -325,7 +327,7 @@ msgstr " на "
|
|||
#. module: mail
|
||||
#: help:mail.message,auto_delete:0
|
||||
msgid "Permanently delete this email after sending it, to save space"
|
||||
msgstr ""
|
||||
msgstr "Навсегда удалить это письмо после отправки, для экономии места"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,references:0 field:mail.message,references:0
|
||||
|
@ -341,18 +343,18 @@ msgstr "Вывести текст"
|
|||
#. module: mail
|
||||
#: view:mail.compose.message:0 view:mail.message:0
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
msgstr "Отмена"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
msgstr "Открыть"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_thread.py:434
|
||||
#, python-format
|
||||
msgid "[OpenERP-Forward-Failed] %s"
|
||||
msgstr ""
|
||||
msgstr "[OpenERP-Forward-Failed] %s"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.message,user_id:0
|
||||
|
@ -366,11 +368,13 @@ msgid ""
|
|||
"Full message headers, e.g. SMTP session headers (usually available on "
|
||||
"inbound messages only)"
|
||||
msgstr ""
|
||||
"Полные заголовки сообщения, т.е. заголовки сессии SMTP (обычно доступные "
|
||||
"только во входящих сообщениях)"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Creation Month"
|
||||
msgstr ""
|
||||
msgstr "Месяц создания"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,email_to:0 field:mail.message,email_to:0
|
||||
|
@ -387,7 +391,7 @@ msgstr "Подробности"
|
|||
#: model:ir.actions.act_window,name:mail.action_view_mailgate_thread
|
||||
#: view:mail.thread:0
|
||||
msgid "Email Threads"
|
||||
msgstr ""
|
||||
msgstr "Цепочки эл.писем"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_from:0 help:mail.message,email_from:0
|
||||
|
@ -396,28 +400,30 @@ msgid ""
|
|||
"Message sender, taken from user preferences. If empty, this is not a mail "
|
||||
"but a message."
|
||||
msgstr ""
|
||||
"Отправитель сообщения, взятый из параметров пользователя. Если пусто, то это "
|
||||
"не почта, а сообщение."
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Body (Plain)"
|
||||
msgstr ""
|
||||
msgstr "Текст (простой)"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/wizard/mail_compose_message.py:153
|
||||
#, python-format
|
||||
msgid "You"
|
||||
msgstr ""
|
||||
msgstr "Вы"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,message_id:0 help:mail.message,message_id:0
|
||||
#: help:mail.message.common,message_id:0
|
||||
msgid "Message unique identifier"
|
||||
msgstr ""
|
||||
msgstr "Уникальный идентификатор сообщения"
|
||||
|
||||
#. module: mail
|
||||
#: view:mail.message:0
|
||||
msgid "Body (Rich)"
|
||||
msgstr ""
|
||||
msgstr "Текст (форматный)"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_message.py:155
|
||||
|
@ -427,6 +433,9 @@ msgid ""
|
|||
" Subject: %s \n"
|
||||
"\t"
|
||||
msgstr ""
|
||||
"%s написал на %s: \n"
|
||||
" Тема: %s \n"
|
||||
"\t"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.actions.act_window,name:mail.act_res_partner_emails
|
||||
|
@ -456,53 +465,74 @@ msgstr "Скрытая копия"
|
|||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_message_common
|
||||
msgid "mail.message.common"
|
||||
msgstr ""
|
||||
msgstr "mail.message.common"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,references:0 help:mail.message,references:0
|
||||
#: help:mail.message.common,references:0
|
||||
msgid "Message references, such as identifiers of previous messages"
|
||||
msgstr ""
|
||||
msgstr "Ссылки сообщения, такие как идентификаторы предыдущих сообщений"
|
||||
|
||||
#. module: mail
|
||||
#: constraint:res.partner:0
|
||||
msgid "Error ! You cannot create recursive associated members."
|
||||
msgstr ""
|
||||
msgstr "Ошибка! Вы не можете создавать рекурсивные ссылки на участников."
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,email_cc:0 help:mail.message,email_cc:0
|
||||
#: help:mail.message.common,email_cc:0
|
||||
msgid "Carbon copy message recipients"
|
||||
msgstr ""
|
||||
msgstr "Получатели скрытой копии сообщения"
|
||||
|
||||
#. module: mail
|
||||
#: selection:mail.message,state:0
|
||||
msgid "Delivery Failed"
|
||||
msgstr ""
|
||||
msgstr "Доставка не удалась"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_message
|
||||
msgid "Email Message"
|
||||
msgstr ""
|
||||
msgstr "Сообщение эл.почты"
|
||||
|
||||
#. module: mail
|
||||
#: model:ir.model,name:mail.model_mail_thread view:mail.thread:0
|
||||
msgid "Email Thread"
|
||||
msgstr ""
|
||||
msgstr "Цепочка эл.почты"
|
||||
|
||||
#. module: mail
|
||||
#: field:mail.compose.message,filter_id:0
|
||||
msgid "Filters"
|
||||
msgstr ""
|
||||
msgstr "Фильтры"
|
||||
|
||||
#. module: mail
|
||||
#: code:addons/mail/mail_thread.py:220
|
||||
#, python-format
|
||||
msgid "Mail attachment"
|
||||
msgstr ""
|
||||
msgstr "Почтовое вложение"
|
||||
|
||||
#. module: mail
|
||||
#: help:mail.compose.message,reply_to:0 help:mail.message,reply_to:0
|
||||
#: help:mail.message.common,reply_to:0
|
||||
msgid "Preferred response address for the message"
|
||||
msgstr ""
|
||||
msgstr "Предпочтительный адрес ответа для сообщения"
|
||||
|
||||
#~ msgid "Message type"
|
||||
#~ msgstr "Тип сообщения"
|
||||
|
||||
#~ msgid "Text contents"
|
||||
#~ msgstr "Текстовое содержимое"
|
||||
|
||||
#~ msgid "Related Document model"
|
||||
#~ msgstr "Модель связанного документа"
|
||||
|
||||
#~ msgid "E-mail composition wizard"
|
||||
#~ msgstr "Мастер создания электронной почты"
|
||||
|
||||
#~ msgid "Rich-text contents"
|
||||
#~ msgstr "Форматное текстовое сообщение"
|
||||
|
||||
#~ msgid "Related user"
|
||||
#~ msgstr "Связанный пользователь"
|
||||
|
||||
#~ msgid "Message headers"
|
||||
#~ msgstr "Заголовки сообщения"
|
||||
|
|
|
@ -84,9 +84,16 @@ class mail_notification(osv.Model):
|
|||
return False
|
||||
|
||||
def set_message_read(self, cr, uid, msg_ids, read=None, context=None):
|
||||
""" TDE note: add a comment, verify method calls, because js seems obfuscated. """
|
||||
""" Set a message and its child messages as (un)read for uid.
|
||||
|
||||
:param bool read: read / unread
|
||||
"""
|
||||
# TDE note: use child_of or front-end send correct values ?
|
||||
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)
|
||||
notif_ids = self.search(cr, uid, [
|
||||
('partner_id', '=', user_pid),
|
||||
('message_id', 'in', msg_ids)
|
||||
], context=context)
|
||||
|
||||
# all message have notifications: already set them as (un)read
|
||||
if len(notif_ids) == len(msg_ids):
|
||||
|
@ -130,7 +137,8 @@ class mail_notification(osv.Model):
|
|||
|
||||
def _notify(self, cr, uid, msg_id, context=None):
|
||||
""" Send by email the notification depending on the user preferences """
|
||||
context = context or {}
|
||||
if context is None:
|
||||
context = {}
|
||||
# mail_noemail (do not send email) or no partner_ids: do not send, return
|
||||
if context.get('mail_noemail'):
|
||||
return True
|
||||
|
@ -140,9 +148,15 @@ class mail_notification(osv.Model):
|
|||
if not notify_partner_ids:
|
||||
return True
|
||||
|
||||
# add the context in the email
|
||||
# TDE FIXME: commented, to be improved in a future branch
|
||||
# quote_context = self.pool.get('mail.message').message_quote_context(cr, uid, msg_id, context=context)
|
||||
|
||||
mail_mail = self.pool.get('mail.mail')
|
||||
# add signature
|
||||
body_html = msg.body
|
||||
# if quote_context:
|
||||
# body_html = tools.append_content_to_html(body_html, quote_context, plaintext=False)
|
||||
signature = msg.author_id and msg.author_id.user_ids[0].signature or ''
|
||||
if signature:
|
||||
body_html = tools.append_content_to_html(body_html, signature)
|
||||
|
|
|
@ -82,9 +82,8 @@
|
|||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"
|
||||
options='{"thread_level": 1}'/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -24,11 +24,17 @@ import tools
|
|||
|
||||
from email.header import decode_header
|
||||
from openerp import SUPERUSER_ID
|
||||
from osv import osv, orm, fields
|
||||
from tools.translate import _
|
||||
from openerp.osv import osv, orm, fields
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from mako.template import Template as MakoTemplate
|
||||
except ImportError:
|
||||
_logger.warning("payment_acquirer: mako templates not available, payment acquirer will not work!")
|
||||
|
||||
|
||||
""" Some tools for parsing / creating email fields """
|
||||
def decode(text):
|
||||
"""Returns unicode() string conversion of the the given encoded smtp header text"""
|
||||
|
@ -46,7 +52,7 @@ class mail_message(osv.Model):
|
|||
_order = 'id desc'
|
||||
|
||||
_message_read_limit = 10
|
||||
_message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read',
|
||||
_message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'email_from',
|
||||
'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids']
|
||||
_message_record_name_length = 18
|
||||
_message_read_more_limit = 1024
|
||||
|
@ -57,17 +63,14 @@ class mail_message(osv.Model):
|
|||
return name[:self._message_record_name_length] + '...'
|
||||
|
||||
def _get_record_name(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Return the related document name, using name_get. It is included in
|
||||
a try/except statement, because if uid cannot read the related
|
||||
document, he should see a void string instead of crashing. """
|
||||
""" Return the related document name, using name_get. It is done using
|
||||
SUPERUSER_ID, to be sure to have the record name correctly stored. """
|
||||
# TDE note: regroup by model/ids, to have less queries to perform
|
||||
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']:
|
||||
if not message.get('model') or not message.get('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])
|
||||
except (orm.except_orm, osv.except_osv):
|
||||
pass
|
||||
result[message['id']] = self._shorten_name(self.pool.get(message['model']).name_get(cr, SUPERUSER_ID, [message['res_id']], context=context)[0][1])
|
||||
return result
|
||||
|
||||
def _get_to_read(self, cr, uid, ids, name, arg, context=None):
|
||||
|
@ -78,10 +81,10 @@ class mail_message(osv.Model):
|
|||
notif_ids = notif_obj.search(cr, uid, [
|
||||
('partner_id', 'in', [partner_id]),
|
||||
('message_id', 'in', ids),
|
||||
('read', '=', False)
|
||||
('read', '=', False),
|
||||
], context=context)
|
||||
for notif in notif_obj.browse(cr, uid, notif_ids, context=context):
|
||||
res[notif.message_id.id] = not notif.read
|
||||
res[notif.message_id.id] = True
|
||||
return res
|
||||
|
||||
def _search_to_read(self, cr, uid, obj, name, domain, context=None):
|
||||
|
@ -115,16 +118,21 @@ class mail_message(osv.Model):
|
|||
], 'Type',
|
||||
help="Message type: email for email message, notification for system "\
|
||||
"message, comment for other messages such as user replies"),
|
||||
'author_id': fields.many2one('res.partner', 'Author', required=True),
|
||||
'partner_ids': fields.many2many('res.partner', 'mail_notification', 'message_id', 'partner_id', 'Recipients'),
|
||||
'email_from': fields.char('From',
|
||||
help="Email address of the sender. This field is set when no matching partner is found for incoming emails."),
|
||||
'author_id': fields.many2one('res.partner', 'Author',
|
||||
help="Author of the message. If not set, email_from may hold an email address that did not match any partner."),
|
||||
'partner_ids': fields.many2many('res.partner', string='Recipients'),
|
||||
'notified_partner_ids': fields.many2many('res.partner', 'mail_notification',
|
||||
'message_id', 'partner_id', 'Recipients'),
|
||||
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel',
|
||||
'message_id', 'attachment_id', 'Attachments'),
|
||||
'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', help="Initial thread message."),
|
||||
'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'),
|
||||
'model': fields.char('Related Document Model', size=128, select=1),
|
||||
'res_id': fields.integer('Related Document ID', select=1),
|
||||
'record_name': fields.function(_get_record_name, type='string',
|
||||
string='Message Record Name',
|
||||
'record_name': fields.function(_get_record_name, type='char',
|
||||
store=True, string='Message Record Name',
|
||||
help="Name get of the related document."),
|
||||
'notification_ids': fields.one2many('mail.notification', 'message_id', 'Notifications'),
|
||||
'subject': fields.char('Subject'),
|
||||
|
@ -198,19 +206,33 @@ class mail_message(osv.Model):
|
|||
|
||||
:param dict message: read result of a mail.message
|
||||
"""
|
||||
# TDE note: this method should be optimized, to lessen the number of queries, will be done ASAP
|
||||
is_author = False
|
||||
if message['author_id']:
|
||||
is_author = message['author_id'][0] == self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
|
||||
author_id = message['author_id']
|
||||
elif message['email_from']:
|
||||
author_id = (0, message['email_from'])
|
||||
|
||||
has_voted = False
|
||||
if uid in message['vote_user_ids']:
|
||||
if uid in message.get('vote_user_ids'):
|
||||
has_voted = True
|
||||
|
||||
is_favorite = False
|
||||
if uid in message['favorite_user_ids']:
|
||||
if uid in message.get('favorite_user_ids'):
|
||||
is_favorite = True
|
||||
|
||||
is_private = True
|
||||
if message.get('model') and message.get('res_id'):
|
||||
is_private = False
|
||||
|
||||
try:
|
||||
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, message['attachment_ids'], context=context)]
|
||||
except (orm.except_orm, osv.except_osv):
|
||||
attachment_ids = []
|
||||
|
||||
# TDE note: should we send partner_ids ?
|
||||
# TDE note: shouldn't we separated followers and other partners ? costly to compute maybe ,
|
||||
try:
|
||||
partner_ids = self.pool.get('res.partner').name_get(cr, uid, message['partner_ids'], context=context)
|
||||
except (orm.except_orm, osv.except_osv):
|
||||
|
@ -226,95 +248,106 @@ class mail_message(osv.Model):
|
|||
'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,
|
||||
# TDE note: is this useful ? to check
|
||||
'author_id': author_id,
|
||||
'is_author': is_author,
|
||||
'partner_ids': partner_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: should only use number of votes
|
||||
'parent_id': False,
|
||||
'vote_nb': len(message['vote_user_ids']),
|
||||
'has_voted': has_voted,
|
||||
'is_private': message['model'] and message['res_id'],
|
||||
'is_private': is_private,
|
||||
'is_favorite': is_favorite,
|
||||
'to_read': message['to_read'],
|
||||
}
|
||||
|
||||
def _message_read_expandable(self, cr, uid, message_list, read_messages,
|
||||
message_loaded_ids=[], domain=[], context=None, parent_id=False, limit=None):
|
||||
""" Create the expandable message for all parent message read
|
||||
this function is used by message_read
|
||||
def _message_read_add_expandables(self, cr, uid, message_list, read_messages,
|
||||
thread_level=0, message_loaded_ids=[], domain=[], parent_id=False, context=None, limit=None):
|
||||
""" Create expandables for message_read, to load new messages.
|
||||
1. get the expandable for new threads
|
||||
if display is flat (thread_level == 0):
|
||||
fetch message_ids < min(already displayed ids), because we
|
||||
want a flat display, ordered by id
|
||||
else:
|
||||
fetch message_ids that are not childs of already displayed
|
||||
messages
|
||||
2. get the expandables for new messages inside threads if display
|
||||
is not flat
|
||||
for each thread header, search for its childs
|
||||
for each hole in the child list based on message displayed,
|
||||
create an expandable
|
||||
|
||||
:param list message_list: list of messages given by message_read to
|
||||
which we have to add expandables
|
||||
:param list message_list:list of message structure for the Chatter
|
||||
widget to which expandables are added
|
||||
:param dict read_messages: dict [id]: read result of the messages to
|
||||
easily have access to their values, given their ID
|
||||
:return bool: True
|
||||
"""
|
||||
# sort for group items / TDE: move to message_read
|
||||
# result = sorted(result, key=lambda k: k['id'])
|
||||
tree_not = []
|
||||
# expandable for not show message
|
||||
def _get_expandable(domain, message_nb, parent_id, id, model):
|
||||
return {
|
||||
'domain': domain,
|
||||
'nb_messages': message_nb,
|
||||
'type': 'expandable',
|
||||
'parent_id': parent_id,
|
||||
'id': id,
|
||||
# TDE note: why do we need model sometimes, and sometimes not ???
|
||||
'model': model,
|
||||
}
|
||||
|
||||
# all_not_loaded_ids = []
|
||||
id_list = sorted(read_messages.keys())
|
||||
if not id_list:
|
||||
return message_list
|
||||
|
||||
# 1. get the expandable for new threads
|
||||
if thread_level == 0:
|
||||
exp_domain = domain + [('id', '<', min(message_loaded_ids + id_list))]
|
||||
else:
|
||||
exp_domain = domain + ['!', ('id', 'child_of', message_loaded_ids + id_list)]
|
||||
ids = self.search(cr, uid, exp_domain, context=context, limit=1)
|
||||
if ids:
|
||||
message_list.append(_get_expandable(exp_domain, -1, parent_id, -1, None))
|
||||
|
||||
# 2. get the expandables for new messages inside threads if display is not flat
|
||||
if thread_level == 0:
|
||||
return True
|
||||
for message_id in id_list:
|
||||
message = read_messages[message_id]
|
||||
|
||||
# message is not a thread header (has a parent_id)
|
||||
# TDE note: parent_id is false is there is a parent we can not see -> ok
|
||||
if message.get('parent_id'):
|
||||
continue
|
||||
|
||||
# TDE note: check search is correctly implemented in mail.message
|
||||
not_loaded_ids = self.search(cr, uid, [
|
||||
('parent_id', '=', message['id']),
|
||||
('id', 'child_of', message['id']),
|
||||
('id', 'not in', message_loaded_ids),
|
||||
], context=context, limit=self._message_read_more_limit)
|
||||
# group childs not read
|
||||
id_min = None
|
||||
id_max = None
|
||||
nb = 0
|
||||
if not not_loaded_ids:
|
||||
continue
|
||||
|
||||
# all_not_loaded_ids += not_loaded_ids
|
||||
# group childs not read
|
||||
id_min, id_max, nb = max(not_loaded_ids), 0, 0
|
||||
for not_loaded_id in not_loaded_ids:
|
||||
if not read_messages.get(not_loaded_id):
|
||||
nb += 1
|
||||
if id_min == None or id_min > not_loaded_id:
|
||||
if id_min > not_loaded_id:
|
||||
id_min = not_loaded_id
|
||||
if id_max == None or id_max < not_loaded_id:
|
||||
if id_max < not_loaded_id:
|
||||
id_max = not_loaded_id
|
||||
tree_not.append(not_loaded_id)
|
||||
elif nb > 0:
|
||||
exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
|
||||
message_list.append(_get_expandable(exp_domain, nb, message_id, id_min, message.get('model')))
|
||||
id_min, id_max, nb = max(not_loaded_ids), 0, 0
|
||||
else:
|
||||
if nb > 0:
|
||||
message_list.append({
|
||||
'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', message_id)],
|
||||
'nb_messages': nb,
|
||||
'type': 'expandable',
|
||||
'parent_id': message_id,
|
||||
'id': id_min,
|
||||
'model': message['model']
|
||||
})
|
||||
id_min = None
|
||||
id_max = None
|
||||
nb = 0
|
||||
id_min, id_max, nb = max(not_loaded_ids), 0, 0
|
||||
if nb > 0:
|
||||
message_list.append({
|
||||
'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', message_id)],
|
||||
'nb_messages': nb,
|
||||
'type': 'expandable',
|
||||
'parent_id': message_id,
|
||||
'id': id_min,
|
||||
'model': message['model'],
|
||||
})
|
||||
exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
|
||||
message_list.append(_get_expandable(exp_domain, nb, message_id, id_min, message.get('model')))
|
||||
|
||||
for msg_id in read_messages.keys() + tree_not:
|
||||
message_loaded_ids.append(msg_id)
|
||||
# message_loaded_ids = list(set(message_loaded_ids + read_messages.keys() + all_not_loaded_ids))
|
||||
|
||||
# expandable for limit max
|
||||
ids = self.search(cr, uid, domain + [('id', 'not in', message_loaded_ids)], context=context, limit=1)
|
||||
if len(ids) > 0:
|
||||
message_list.append({
|
||||
'domain': domain,
|
||||
'nb_messages': 0,
|
||||
'type': 'expandable',
|
||||
'parent_id': parent_id,
|
||||
'id': -1,
|
||||
'max_limit': True,
|
||||
})
|
||||
|
||||
return message_list
|
||||
return True
|
||||
|
||||
def _get_parent(self, cr, uid, message, context=None):
|
||||
""" Tools method that tries to get the parent of a mail.message. If
|
||||
|
@ -331,57 +364,70 @@ class mail_message(osv.Model):
|
|||
except (orm.except_orm, osv.except_osv):
|
||||
return False
|
||||
|
||||
def message_read(self, cr, uid, ids=False, domain=[], message_loaded_ids=[], 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,
|
||||
def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None, thread_level=0, context=None, parent_id=False, limit=None):
|
||||
""" Read messages from mail.message, and get back a list of structured
|
||||
messages to be displayed as discussion threads. If IDs is set,
|
||||
fetch these records. Otherwise use the domain to fetch messages.
|
||||
After having fetch messages, their parents & child will be added to obtain
|
||||
well formed threads.
|
||||
After having fetch messages, their ancestors will be added to obtain
|
||||
well formed threads, if uid has access to them.
|
||||
|
||||
TDE note: update this comment after final method implementation
|
||||
After reading the messages, expandable messages are added in the
|
||||
message list (see ``_message_read_add_expandables``). It consists
|
||||
in messages holding the 'read more' data: number of messages to
|
||||
read, domain to apply.
|
||||
|
||||
: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
|
||||
:param list ids: optional IDs to fetch
|
||||
:param list domain: optional domain for searching ids if ids not set
|
||||
:param list message_unload_ids: optional ids we do not want to fetch,
|
||||
because i.e. they are already displayed somewhere
|
||||
:param int parent_id: context of parent_id
|
||||
- if parent_id reached when adding ancestors, stop going further
|
||||
in the ancestor search
|
||||
- if set in flat mode, ancestor_id is set to parent_id
|
||||
:param int limit: number of messages to fetch, before adding the
|
||||
ancestors and expandables
|
||||
:return list: list of message structure for the Chatter widget
|
||||
"""
|
||||
if message_loaded_ids:
|
||||
domain += [('id', 'not in', message_loaded_ids)]
|
||||
# print 'message_read', ids, domain, message_unload_ids, thread_level, context, parent_id, limit
|
||||
assert thread_level in [0, 1], 'message_read() thread_level should be 0 (flat) or 1 (1 level of thread); given %s.' % thread_level
|
||||
domain = domain if domain is not None else []
|
||||
message_unload_ids = message_unload_ids if message_unload_ids is not None else []
|
||||
if message_unload_ids:
|
||||
domain += [('id', 'not in', message_unload_ids)]
|
||||
limit = limit or self._message_read_limit
|
||||
read_messages = {}
|
||||
message_list = []
|
||||
|
||||
# specific IDs given: fetch those ids and return directly the message list
|
||||
if ids:
|
||||
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))
|
||||
message_list = sorted(message_list, key=lambda k: k['id'])
|
||||
return message_list
|
||||
|
||||
# TDE FIXME: check access rights on search are implemented for mail.message
|
||||
# fetch messages according to the domain, add their parents if uid has access to
|
||||
ids = self.search(cr, uid, domain, context=context, limit=limit)
|
||||
# no specific IDS given: fetch messages according to the domain, add their parents if uid has access to
|
||||
if ids is None:
|
||||
ids = self.search(cr, uid, domain, context=context, limit=limit)
|
||||
for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
|
||||
# if not in tree and not in message_loded list
|
||||
if not read_messages.get(message.get('id')) and message.get('id') not in message_loaded_ids:
|
||||
read_messages[message.get('id')] = message
|
||||
message_id = message['id']
|
||||
|
||||
# if not in tree and not in message_loaded list
|
||||
if not message_id in read_messages and not message_id in message_unload_ids:
|
||||
read_messages[message_id] = message
|
||||
message_list.append(self._message_get_dict(cr, uid, message, context=context))
|
||||
|
||||
# get all parented message if the user have the access
|
||||
# get the older ancestor the user can read, update its ancestor field
|
||||
if not thread_level:
|
||||
message_list[-1]['parent_id'] = parent_id
|
||||
continue
|
||||
parent = self._get_parent(cr, uid, message, context=context)
|
||||
while parent and parent.get('id') != parent_id:
|
||||
if not read_messages.get(parent.get('id')) and parent.get('id') not in message_loaded_ids:
|
||||
read_messages[parent.get('id')] = parent
|
||||
message_list.append(self._message_get_dict(cr, uid, parent, context=context))
|
||||
parent = self._get_parent(cr, uid, parent, context=context)
|
||||
message_list[-1]['parent_id'] = parent.get('id')
|
||||
message = parent
|
||||
parent = self._get_parent(cr, uid, message, context=context)
|
||||
# if in thread: add its ancestor to the list of messages
|
||||
if not message['id'] in read_messages and not message['id'] in message_unload_ids:
|
||||
read_messages[message['id']] = message
|
||||
message_list.append(self._message_get_dict(cr, uid, message, context=context))
|
||||
|
||||
# get the child expandable messages for the tree
|
||||
message_list = sorted(message_list, key=lambda k: k['id'])
|
||||
message_list = self._message_read_expandable(cr, uid, message_list, read_messages,
|
||||
message_loaded_ids=message_loaded_ids, domain=domain, context=context, parent_id=parent_id, limit=limit)
|
||||
self._message_read_add_expandables(cr, uid, message_list, read_messages, thread_level=thread_level,
|
||||
message_loaded_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context, limit=limit)
|
||||
|
||||
# message_list = sorted(message_list, key=lambda k: k['id'])
|
||||
return message_list
|
||||
|
||||
# TDE Note: do we need this ?
|
||||
|
@ -394,7 +440,7 @@ class mail_message(osv.Model):
|
|||
# return attachment_list
|
||||
|
||||
#------------------------------------------------------
|
||||
# Email api
|
||||
# mail_message internals
|
||||
#------------------------------------------------------
|
||||
|
||||
def init(self, cr):
|
||||
|
@ -402,23 +448,75 @@ class mail_message(osv.Model):
|
|||
if not cr.fetchone():
|
||||
cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""")
|
||||
|
||||
def _search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False, access_rights_uid=None):
|
||||
""" Override that adds specific access rights of mail.message, to remove
|
||||
ids uid could not see according to our custom rules. Please refer
|
||||
to check_access_rule for more details about those rules.
|
||||
|
||||
After having received ids of a classic search, keep only:
|
||||
- if author_id == pid, uid is the author, OR
|
||||
- a notification (id, pid) exists, uid has been notified, OR
|
||||
- uid have read access to the related document is model, res_id
|
||||
- otherwise: remove the id
|
||||
"""
|
||||
# Rules do not apply to administrator
|
||||
# print '_search', uid, args
|
||||
if uid == SUPERUSER_ID:
|
||||
return super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order,
|
||||
context=context, count=count, access_rights_uid=access_rights_uid)
|
||||
# Perform a super with count as False, to have the ids, not a counter
|
||||
ids = super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order,
|
||||
context=context, count=False, access_rights_uid=access_rights_uid)
|
||||
if not ids and count:
|
||||
return 0
|
||||
elif not ids:
|
||||
return ids
|
||||
|
||||
pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'])['partner_id'][0]
|
||||
author_ids, partner_ids, allowed_ids = set([]), set([]), set([])
|
||||
model_ids = {}
|
||||
|
||||
messages = super(mail_message, self).read(cr, uid, ids, ['author_id', 'model', 'res_id', 'notified_partner_ids'], context=context)
|
||||
for message in messages:
|
||||
if message.get('author_id') and message.get('author_id')[0] == pid:
|
||||
author_ids.add(message.get('id'))
|
||||
elif pid in message.get('notified_partner_ids'):
|
||||
partner_ids.add(message.get('id'))
|
||||
elif message.get('model') and message.get('res_id'):
|
||||
model_ids.setdefault(message.get('model'), {}).setdefault(message.get('res_id'), set()).add(message.get('id'))
|
||||
|
||||
model_access_obj = self.pool.get('ir.model.access')
|
||||
for doc_model, doc_dict in model_ids.iteritems():
|
||||
if not model_access_obj.check(cr, uid, doc_model, 'read', False):
|
||||
continue
|
||||
doc_ids = doc_dict.keys()
|
||||
allowed_doc_ids = self.pool.get(doc_model).search(cr, uid, [('id', 'in', doc_ids)], context=context)
|
||||
allowed_ids |= set([message_id for allowed_doc_id in allowed_doc_ids for message_id in doc_dict[allowed_doc_id]])
|
||||
|
||||
final_ids = author_ids | partner_ids | allowed_ids
|
||||
if count:
|
||||
return len(final_ids)
|
||||
else:
|
||||
return list(final_ids)
|
||||
|
||||
def check_access_rule(self, cr, uid, ids, operation, context=None):
|
||||
""" Access rules of mail.message:
|
||||
- read: if
|
||||
- notification exist (I receive pushed message) OR
|
||||
- author_id = pid (I am the author) OR
|
||||
- I can read the related document if res_model, res_id
|
||||
- Otherwise: raise
|
||||
- author_id == pid, uid is the author, OR
|
||||
- mail_notification (id, pid) exists, uid has been notified, OR
|
||||
- uid have read access to the related document if model, res_id
|
||||
- otherwise: raise
|
||||
- create: if
|
||||
- I am in the document message_follower_ids OR
|
||||
- I can write on the related document if res_model, res_id OR
|
||||
- I create a private message (no model, no res_id)
|
||||
- Otherwise: raise
|
||||
- no model, no res_id, I create a private message
|
||||
- pid in message_follower_ids if model, res_id OR
|
||||
- uid have write access on the related document if model, res_id, OR
|
||||
- otherwise: raise
|
||||
- write: if
|
||||
- I can write on the related document if res_model, res_id
|
||||
- uid has write access on the related document if model, res_id
|
||||
- Otherwise: raise
|
||||
- unlink: if
|
||||
- I can write on the related document if res_model, res_id
|
||||
- uid has write access on the related document if model, res_id
|
||||
- Otherwise: raise
|
||||
"""
|
||||
if uid == SUPERUSER_ID:
|
||||
|
@ -428,15 +526,25 @@ class mail_message(osv.Model):
|
|||
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
|
||||
|
||||
# Read mail_message.ids to have their values
|
||||
model_record_ids = {}
|
||||
message_values = dict.fromkeys(ids)
|
||||
model_record_ids = {}
|
||||
cr.execute('SELECT DISTINCT id, model, res_id, author_id FROM "%s" WHERE id = ANY (%%s)' % self._table, (ids,))
|
||||
for id, rmod, rid, author_id in cr.fetchall():
|
||||
message_values[id] = {'res_model': rmod, 'res_id': rid, 'author_id': author_id}
|
||||
if rmod:
|
||||
model_record_ids.setdefault(rmod, set()).add(rid)
|
||||
model_record_ids.setdefault(rmod, dict()).setdefault(rid, set()).add(id)
|
||||
|
||||
# Read: Check for received notifications -> could become an ir.rule, but not till we do not have a many2one variable field
|
||||
# Author condition, for read and create (private message) -> could become an ir.rule, but not till we do not have a many2one variable field
|
||||
if operation == 'read':
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
if message.get('author_id') and message.get('author_id') == partner_id]
|
||||
elif operation == 'create':
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
if not message.get('model') and not message.get('res_id')]
|
||||
else:
|
||||
author_ids = []
|
||||
|
||||
# Notification condition, for read (check for received notifications and create (in message_follower_ids)) -> could become an ir.rule, but not till we do not have a many2one variable field
|
||||
if operation == 'read':
|
||||
not_obj = self.pool.get('mail.notification')
|
||||
not_ids = not_obj.search(cr, SUPERUSER_ID, [
|
||||
|
@ -444,38 +552,24 @@ class mail_message(osv.Model):
|
|||
('message_id', 'in', ids),
|
||||
], context=context)
|
||||
notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)]
|
||||
else:
|
||||
notified_ids = []
|
||||
# Read: Check messages you are author -> could become an ir.rule, but not till we do not have a many2one variable field
|
||||
if operation == 'read':
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
if message.get('author_id') and message.get('author_id') == partner_id]
|
||||
# Create: Check messages you create that are private messages -> ir.rule ?
|
||||
elif operation == 'create':
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
if not message.get('model') and not message.get('res_id')]
|
||||
else:
|
||||
author_ids = []
|
||||
|
||||
# Create: Check message_follower_ids
|
||||
if operation == 'create':
|
||||
doc_follower_ids = []
|
||||
for model, mids in model_record_ids.items():
|
||||
notified_ids = []
|
||||
for doc_model, doc_dict in model_record_ids.items():
|
||||
fol_obj = self.pool.get('mail.followers')
|
||||
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
|
||||
('res_model', '=', model),
|
||||
('res_id', 'in', list(mids)),
|
||||
('res_model', '=', doc_model),
|
||||
('res_id', 'in', list(doc_dict.keys())),
|
||||
('partner_id', '=', partner_id),
|
||||
], context=context)
|
||||
fol_mids = [follower.res_id for follower in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)]
|
||||
doc_follower_ids += [mid for mid, message in message_values.iteritems()
|
||||
if message.get('res_model') == model and message.get('res_id') in fol_mids]
|
||||
notified_ids += [mid for mid, message in message_values.iteritems()
|
||||
if message.get('res_model') == doc_model and message.get('res_id') in fol_mids]
|
||||
else:
|
||||
doc_follower_ids = []
|
||||
notified_ids = []
|
||||
|
||||
# Calculate remaining ids, and related model/res_ids
|
||||
model_record_ids = {}
|
||||
other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids))
|
||||
other_ids = set(ids).difference(set(author_ids), set(notified_ids))
|
||||
for id in other_ids:
|
||||
if message_values[id]['res_model']:
|
||||
model_record_ids.setdefault(message_values[id]['res_model'], set()).add(message_values[id]['res_id'])
|
||||
|
@ -495,7 +589,7 @@ class mail_message(osv.Model):
|
|||
if message.get('res_model') == model and message.get('res_id') in mids]
|
||||
|
||||
# Calculate remaining ids: if not void, raise an error
|
||||
other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids), set(document_related_ids))
|
||||
other_ids = other_ids - set(document_related_ids)
|
||||
if not other_ids:
|
||||
return
|
||||
raise orm.except_orm(_('Access Denied'),
|
||||
|
@ -529,50 +623,123 @@ class mail_message(osv.Model):
|
|||
self.pool.get('ir.attachment').unlink(cr, uid, attachments_to_delete, context=context)
|
||||
return super(mail_message, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def _notify_followers(self, cr, uid, newid, message, context=None):
|
||||
""" Add the related record followers to the destination partner_ids.
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
""" Overridden to avoid duplicating fields that are unique to each email """
|
||||
if default is None:
|
||||
default = {}
|
||||
default.update(message_id=False, headers=False)
|
||||
return super(mail_message, self).copy(cr, uid, id, default=default, context=context)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Messaging API
|
||||
#------------------------------------------------------
|
||||
|
||||
# TDE note: this code is not used currently, will be improved in a future merge, when quoted context
|
||||
# will be added to email send for notifications. Currently only WIP.
|
||||
MAIL_TEMPLATE = """<div>
|
||||
% if message:
|
||||
${display_message(message)}
|
||||
% endif
|
||||
% for ctx_msg in context_messages:
|
||||
${display_message(ctx_msg)}
|
||||
% endfor
|
||||
% if add_expandable:
|
||||
${display_expandable()}
|
||||
% endif
|
||||
${display_message(header_message)}
|
||||
</div>
|
||||
|
||||
<%def name="display_message(message)">
|
||||
<div>
|
||||
Subject: ${message.subject}<br />
|
||||
Body: ${message.body}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="display_expandable()">
|
||||
<div>This is an expandable.</div>
|
||||
</%def>
|
||||
"""
|
||||
|
||||
def message_quote_context(self, cr, uid, id, context=None, limit=3, add_original=False):
|
||||
"""
|
||||
partners_to_notify = set([])
|
||||
# message has no subtype_id: pure log message -> no partners, no one notified
|
||||
if not message.subtype_id:
|
||||
message.write({'partner_ids': [5]})
|
||||
return True
|
||||
# all partner_ids of the mail.message have to be notified
|
||||
if message.partner_ids:
|
||||
partners_to_notify |= set(partner.id for partner in message.partner_ids)
|
||||
# all followers of the mail.message document have to be added as partners and notified
|
||||
if message.model and message.res_id:
|
||||
fol_obj = self.pool.get("mail.followers")
|
||||
fol_ids = fol_obj.search(cr, uid, [('res_model', '=', message.model), ('res_id', '=', message.res_id), ('subtype_ids', 'in', message.subtype_id.id)], context=context)
|
||||
fol_objs = fol_obj.browse(cr, uid, fol_ids, context=context)
|
||||
extra_notified = set(fol.partner_id.id for fol in fol_objs)
|
||||
missing_notified = extra_notified - partners_to_notify
|
||||
if missing_notified:
|
||||
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(4, p_id) for p_id in missing_notified]}, context=context)
|
||||
1. message.parent_id = False: new thread, no quote_context
|
||||
2. get the lasts messages in the thread before message
|
||||
3. get the message header
|
||||
4. add an expandable between them
|
||||
|
||||
:param dict quote_context: options for quoting
|
||||
:return string: html quote
|
||||
"""
|
||||
add_expandable = False
|
||||
|
||||
message = self.browse(cr, uid, id, context=context)
|
||||
if not message.parent_id:
|
||||
return ''
|
||||
context_ids = self.search(cr, uid, [
|
||||
('parent_id', '=', message.parent_id.id),
|
||||
('id', '<', message.id),
|
||||
], limit=limit, context=context)
|
||||
|
||||
if len(context_ids) >= limit:
|
||||
add_expandable = True
|
||||
context_ids = context_ids[0:-1]
|
||||
|
||||
context_ids.append(message.parent_id.id)
|
||||
context_messages = self.browse(cr, uid, context_ids, context=context)
|
||||
header_message = context_messages.pop()
|
||||
|
||||
try:
|
||||
if not add_original:
|
||||
message = False
|
||||
result = MakoTemplate(self.MAIL_TEMPLATE).render_unicode(message=message,
|
||||
context_messages=context_messages,
|
||||
header_message=header_message,
|
||||
add_expandable=add_expandable,
|
||||
# context kw would clash with mako internals
|
||||
ctx=context,
|
||||
format_exceptions=True)
|
||||
result = result.strip()
|
||||
return result
|
||||
except Exception:
|
||||
_logger.exception("failed to render mako template for quoting message")
|
||||
return ''
|
||||
return result
|
||||
|
||||
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.model and message.res_id:
|
||||
self._notify_followers(cr, uid, newid, message, context=context)
|
||||
message = self.read(cr, uid, newid, ['model', 'res_id', 'author_id', 'subtype_id', 'partner_ids'], context=context)
|
||||
|
||||
# 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)
|
||||
partners_to_notify = set([])
|
||||
# message has no subtype_id: pure log message -> no partners, no one notified
|
||||
if not message.get('subtype_id'):
|
||||
return True
|
||||
# all partner_ids of the mail.message have to be notified
|
||||
if message.get('partner_ids'):
|
||||
partners_to_notify |= set(message.get('partner_ids'))
|
||||
# all followers of the mail.message document have to be added as partners and notified
|
||||
if message.get('model') and message.get('res_id'):
|
||||
fol_obj = self.pool.get("mail.followers")
|
||||
fol_ids = fol_obj.search(cr, uid, [
|
||||
('res_model', '=', message.get('model')),
|
||||
('res_id', '=', message.get('res_id')),
|
||||
('subtype_ids', 'in', message.get('subtype_id')[0])
|
||||
], context=context)
|
||||
fol_objs = fol_obj.read(cr, uid, fol_ids, ['partner_id'], context=context)
|
||||
partners_to_notify |= set(fol['partner_id'][0] for fol in fol_objs)
|
||||
# when writing to a wall
|
||||
if message.get('author_id') and message.get('model') == "res.partner" and message.get('res_id') == message.get('author_id')[0]:
|
||||
partners_to_notify |= set([message.get('author_id')[0]])
|
||||
elif message.get('author_id'):
|
||||
partners_to_notify = partners_to_notify - set([message.get('author_id')[0]])
|
||||
|
||||
if partners_to_notify:
|
||||
self.write(cr, SUPERUSER_ID, [newid], {'notified_partner_ids': [(4, p_id) for p_id in partners_to_notify]}, context=context)
|
||||
|
||||
self.pool.get('mail.notification')._notify(cr, uid, newid, context=context)
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
"""Overridden to avoid duplicating fields that are unique to each email"""
|
||||
if default is None:
|
||||
default = {}
|
||||
default.update(message_id=False, headers=False)
|
||||
return super(mail_message, self).copy(cr, uid, id, default=default, context=context)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Tools
|
||||
#------------------------------------------------------
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
<group>
|
||||
<field name="subject"/>
|
||||
<field name="author_id"/>
|
||||
<field name="email_from"/>
|
||||
<field name="date"/>
|
||||
<field name="type"/>
|
||||
<field name="subtype_id"/>
|
||||
|
@ -38,6 +39,7 @@
|
|||
<field name="res_id"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="partner_ids" widget="many2many_tags"/>
|
||||
<field name="notified_partner_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="body"/>
|
||||
|
@ -63,6 +65,9 @@
|
|||
<filter string="Comments"
|
||||
name="comments" help="Comments"
|
||||
domain="[('type', '=', 'comment')]"/>
|
||||
<filter string="Has attachments"
|
||||
name="attachments"
|
||||
domain="[('attachment_ids', '!=', False)]"/>
|
||||
<filter string="Notifications"
|
||||
name="notifications" help="Notifications"
|
||||
domain="[('type', '=', 'notification')]"/>
|
||||
|
|
|
@ -554,7 +554,11 @@ class mail_thread(osv.AbstractModel):
|
|||
('file2', 'bytes')}
|
||||
}
|
||||
"""
|
||||
msg_dict = {}
|
||||
msg_dict = {
|
||||
'type': 'email',
|
||||
'subtype': 'mail.mt_comment',
|
||||
'author_id': False,
|
||||
}
|
||||
if not isinstance(message, Message):
|
||||
if isinstance(message, unicode):
|
||||
# Warning: message_from_string doesn't always work correctly on unicode,
|
||||
|
@ -572,7 +576,7 @@ class mail_thread(osv.AbstractModel):
|
|||
if 'Subject' in message:
|
||||
msg_dict['subject'] = decode(message.get('Subject'))
|
||||
|
||||
# Envelope fields not stored in mail.message but made available for message_new()
|
||||
# Envelope fields not stored in mail.message but made available for message_new()
|
||||
msg_dict['from'] = decode(message.get('from'))
|
||||
msg_dict['to'] = decode(message.get('to'))
|
||||
msg_dict['cc'] = decode(message.get('cc'))
|
||||
|
@ -581,6 +585,8 @@ class mail_thread(osv.AbstractModel):
|
|||
author_ids = self._message_find_partners(cr, uid, message, ['From'], context=context)
|
||||
if author_ids:
|
||||
msg_dict['author_id'] = author_ids[0]
|
||||
else:
|
||||
msg_dict['email_from'] = message.get('from')
|
||||
partner_ids = self._message_find_partners(cr, uid, message, ['From', 'To', 'Cc'], context=context)
|
||||
msg_dict['partner_ids'] = partner_ids
|
||||
|
||||
|
@ -670,6 +676,18 @@ class mail_thread(osv.AbstractModel):
|
|||
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
|
||||
# we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
|
||||
elif parent_id:
|
||||
message_ids = mail_message.search(cr, SUPERUSER_ID, [('id', '=', parent_id), ('parent_id', '!=', False)], context=context)
|
||||
# avoid loops when finding ancestors
|
||||
processed_list = []
|
||||
if message_ids:
|
||||
_counter, _counter_max = 0, 200
|
||||
message = mail_message.browse(cr, SUPERUSER_ID, message_ids[0], context=context)
|
||||
while (message.parent_id and message.parent_id.id not in processed_list):
|
||||
processed_list.append(message.parent_id.id)
|
||||
message = message.parent_id
|
||||
parent_id = message.id
|
||||
|
||||
values = kwargs
|
||||
values.update({
|
||||
|
@ -689,27 +707,28 @@ class mail_thread(osv.AbstractModel):
|
|||
|
||||
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]
|
||||
def message_post_api(self, cr, uid, thread_id, body='', subject=False, parent_id=False, attachment_ids=None, context=None):
|
||||
""" Wrapper on message_post, used only in Chatter (JS). The purpose is
|
||||
to handle attachments.
|
||||
# TDE FIXME: body is plaintext: convert it into html
|
||||
"""
|
||||
new_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type='comment',
|
||||
subtype='mail.mt_comment', parent_id=parent_id, context=context)
|
||||
|
||||
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:
|
||||
# HACK FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
|
||||
if attachment_ids:
|
||||
ir_attachment = self.pool.get('ir.attachment')
|
||||
mail_message = self.pool.get('mail.message')
|
||||
attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [('res_model', '=', 'mail.message'), ('res_id', '=', 0), ('create_uid', '=', uid), ('id', 'in', attachments)], context=context)
|
||||
if attachment_ids:
|
||||
filtered_attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [
|
||||
('res_model', '=', 'mail.compose.message'),
|
||||
('res_id', '=', 0),
|
||||
('create_uid', '=', uid),
|
||||
('id', 'in', attachment_ids)], context=context)
|
||||
if filtered_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
|
||||
return new_message_id
|
||||
|
||||
#------------------------------------------------------
|
||||
# Followers API
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<!-- MENU -->
|
||||
|
||||
<!-- Top menu item -->
|
||||
<menuitem name="Mails"
|
||||
<menuitem name="Emails"
|
||||
id="mail.mail_feeds_main"
|
||||
groups="base.group_user"
|
||||
sequence="10"/>
|
||||
|
|
|
@ -9,9 +9,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet" position="after">
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids" widget="mail_thread"
|
||||
options='{"thread_level": 1}'/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
|
|
|
@ -111,8 +111,7 @@ class res_users(osv.Model):
|
|||
alias_pool.unlink(cr, uid, alias_ids, context=context)
|
||||
return res
|
||||
|
||||
def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification',
|
||||
subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
|
||||
def message_post_api(self, cr, uid, thread_id, body='', subject=False, parent_id=False, attachment_ids=None, context=None):
|
||||
""" 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.
|
||||
|
@ -124,7 +123,7 @@ class res_users(osv.Model):
|
|||
thread_id = thread_id[0]
|
||||
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)
|
||||
parent_id=parent_id, attachment_ids=attachment_ids, context=context)
|
||||
|
||||
def message_post(self, cr, uid, thread_id, context=None, **kwargs):
|
||||
""" Redirect the posting of message on res.users to the related partner.
|
||||
|
|
|
@ -2,6 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|||
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_user,mail.mail,model_mail_mail,base.group_user,1,1,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,,1,0,0,0
|
||||
access_mail_followers_system,mail.followers.system,model_mail_followers,base.group_system,1,1,1,1
|
||||
|
@ -12,6 +13,6 @@ access_mail_group_user,mail.group.user,model_mail_group,base.group_user,1,1,1,1
|
|||
access_mail_alias_all,mail.alias.all,model_mail_alias,,1,0,0,0
|
||||
access_mail_alias_user,mail.alias,model_mail_alias,base.group_user,1,1,1,0
|
||||
access_mail_alias_system,mail.alias,model_mail_alias,base.group_system,1,1,1,1
|
||||
access_mail_message_subtype,mail.message.subtype,model_mail_message_subtype,,1,1,1,1
|
||||
access_mail_mail_user,mail.mail,model_mail_mail,base.group_user,1,1,1,0
|
||||
access_mail_message_subtype_all,mail.message.subtype.all,model_mail_message_subtype,,1,0,0,0
|
||||
access_mail_vote_all,mail.vote.all,model_mail_vote,,1,1,1,1
|
||||
access_mail_favorite_all,mail.favorite.all,model_mail_favorite,,1,1,1,1
|
|
|
@ -1,439 +1,382 @@
|
|||
/* ------------------------------------------------------------ */
|
||||
/* Reset because of ugly display of end of August
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_mail_wall ul, .openerp .oe_mail_wall li {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
/* ------------ MAIL WIDGET --------------- */
|
||||
.openerp .oe_mail, .openerp .oe_mail *{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.openerp .oe_chatter ul, .openerp .oe_chatter li {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Wall
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp div.oe_mail_wall {
|
||||
overflow: auto;
|
||||
padding: 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_wall div.oe_mail_wall_aside {
|
||||
margin-left: 565px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_wall ul.oe_mail_wall_threads {
|
||||
float: left;
|
||||
width: 560px;
|
||||
margin: 8px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Followers
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp div.oe_mail_recthread_aside h4 {
|
||||
display: inline-block;
|
||||
}
|
||||
.openerp div.oe_mail_recthread_aside button {
|
||||
.openerp .oe_mail {
|
||||
display: block;
|
||||
position: relative;
|
||||
margin: 0px;
|
||||
}
|
||||
.openerp div.oe_mail_recthread_aside label,
|
||||
.openerp div.oe_mail_recthread_aside input {
|
||||
cursor:pointer;
|
||||
.openerp .oe_mail .oe_thread{
|
||||
margin-left: 32px;
|
||||
}
|
||||
.openerp .oe_mail > .oe_thread{
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
/* Specific display of threads in the wall */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ---------------- MESSAGES ------------------ */
|
||||
|
||||
.openerp ul.oe_mail_wall_threads .oe_msg_content textarea.oe_mail_compose_textarea {
|
||||
width: 434px;
|
||||
height: 30px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.openerp li.oe_mail_wall_thread:first .oe_msg_notification {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.openerp div.oe_thread_placeholder img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.openerp div.oe_thread_placeholder div.oe_msg_content {
|
||||
width: 440px;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* RecordThread
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_form div.oe_chatter {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_record_wall {
|
||||
margin: auto;
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_record_wall > .oe_mail_wall_threads {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_recthread_aside {
|
||||
float: right;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_recthread_actions {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_recthread_actions button {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_recthread_aside .oe_follower.oe_follow {
|
||||
color: white;
|
||||
background-color: #8a89ba;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#8a89ba), to(#807fb4));
|
||||
background-image: -webkit-linear-gradient(top, #8a89ba, #807fb4);
|
||||
background-image: -moz-linear-gradient(top, #8a89ba, #807fb4);
|
||||
background-image: -ms-linear-gradient(top, #8a89ba, #807fb4);
|
||||
background-image: -o-linear-gradient(top, #8a89ba, #807fb4);
|
||||
background-image: linear-gradient(to bottom, #8a89ba, #807fb4);
|
||||
}
|
||||
.openerp .oe_mail_recthread_aside .oe_follower.oe_following {
|
||||
color: white;
|
||||
background-color: #dc5f59;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
|
||||
background-image: -webkit-linear-gradient(top, #dc5f59, #b33630);
|
||||
background-image: -moz-linear-gradient(top, #dc5f59, #b33630);
|
||||
background-image: -ms-linear-gradient(top, #dc5f59, #b33630);
|
||||
background-image: -o-linear-gradient(top, #dc5f59, #b33630);
|
||||
background-image: linear-gradient(to bottom, #dc5f59, #b33630);
|
||||
}
|
||||
|
||||
|
||||
.openerp .oe_mail_recthread_aside .oe_follower span {
|
||||
display:none;
|
||||
}
|
||||
.openerp .oe_mail_recthread_aside .oe_following span.oe_following,
|
||||
.openerp .oe_mail_recthread_aside .oe_notfollow span.oe_follow {
|
||||
display:block;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_recthread_followers {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* subtypes
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_mail_subtypes {
|
||||
display:inline-block;
|
||||
.openerp .oe_mail .oe_msg{
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
background: #F4F5FA;
|
||||
border-radius: 2px;
|
||||
margin-bottom: 2px;
|
||||
min-height: 42px;
|
||||
border: solid 1px rgba(0,0,0,0.03);
|
||||
}
|
||||
.openerp .oe_mail_subtypes .oe_recthread_subtypes {
|
||||
background: #fff;
|
||||
padding: 2px;
|
||||
border: 1px solid #aaaaaa;
|
||||
border-top: 0px;
|
||||
.openerp .oe_mail .oe_msg .oe_msg_left{
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
left:0; top: 0; bottom: 0; width: 40px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.openerp .oe_mail_subtypes.oe_mouseout .oe_recthread_subtypes {
|
||||
display: none;
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icon{
|
||||
width: 32px;
|
||||
margin: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.openerp .oe_mail_subtypes.oe_mouseover .oe_recthread_subtypes {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Thread
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp div.oe_mail_thread_action {
|
||||
white-space: normal;
|
||||
padding: 8px;
|
||||
z-index:5;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_thread_action:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */
|
||||
.openerp .oe_msg_content textarea.oe_mail_compose_textarea:focus,
|
||||
.openerp .oe_msg_content div.oe_mail_compose_message_body textarea:focus {
|
||||
outline: 0;
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
}
|
||||
|
||||
.openerp .oe_mail_vote_count,
|
||||
.openerp .oe_msg_vote{
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.openerp button.oe_mail_starbox{
|
||||
background: #ff0000;
|
||||
}
|
||||
.openerp button.oe_mail_starbox.oe_stared{
|
||||
background: #00FF00;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_thread_display {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.openerp div.oe_thread_placeholder {
|
||||
margin-left: 66px;
|
||||
}
|
||||
|
||||
.openerp li.oe_mail_thread_msg {
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
.openerp div.oe_thread_placeholder li.oe_mail_thread_msg:last-child {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_thread_more {
|
||||
display: none;
|
||||
border-bottom: 1px solid #D2D9E7;
|
||||
}
|
||||
|
||||
.openerp li.oe_mail_thread_msg:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.openerp li.oe_mail_thread_msg.oe_mail_read,
|
||||
.openerp li.oe_mail_thread_msg.oe_mail_read div {
|
||||
border-left: #F0F0F0;
|
||||
}
|
||||
.openerp li.oe_mail_thread_msg.oe_mail_read li.oe_mail_thread_msg.oe_mail_unread,
|
||||
.openerp li.oe_mail_thread_msg.oe_mail_read li.oe_mail_thread_msg.oe_mail_unread div {
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
|
||||
.openerp li.oe_mail_thread_msg.oe_mail_unread>div>ul>li.oe_unread,
|
||||
.openerp li.oe_mail_thread_msg.oe_mail_read>div>ul>li.oe_read {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.openerp li.oe_mail_thread_msg > div:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_msg {
|
||||
padding: 0;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.openerp .oe_msg_notification,
|
||||
.openerp .oe_msg_expandable,
|
||||
.openerp .oe_msg_comment,
|
||||
.openerp .oe_msg_email {
|
||||
padding: 8px;
|
||||
background: white;
|
||||
.openerp .oe_mail .oe_msg .oe_msg_center{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.openerp .oe_msg_notification:after,
|
||||
.openerp .oe_msg_comment:after,
|
||||
.openerp .oe_msg_email:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
margin-left: 40px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_footer{
|
||||
padding-left: 4px;
|
||||
overflow: hidden;
|
||||
opacity:0.8;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_content{
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
padding-bottom:1px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_title{
|
||||
font-size: 13px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.openerp div.oe_msg_content {
|
||||
float: left;
|
||||
/* a) Indented Messages */
|
||||
|
||||
.openerp .oe_mail .oe_msg_indented{
|
||||
background: #FFF;
|
||||
border: none;
|
||||
margin-bottom:0px;
|
||||
min-height:38px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_icon{
|
||||
width:32px;
|
||||
margin:2px;
|
||||
border-radius:2px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_subtle{
|
||||
color: #B7B7D5;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_indented .oe_msg_center{
|
||||
margin-left:34px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_content{
|
||||
padding-top:2px;
|
||||
}
|
||||
/* b) Votes (likes) */
|
||||
.openerp .oe_mail .oe_mail_vote_count{
|
||||
display: inline;
|
||||
position: relative;
|
||||
width: 486px;
|
||||
}
|
||||
|
||||
.openerp div.oe_msg_content > li {
|
||||
float: left;
|
||||
background: #7C7BAD;
|
||||
color: white;
|
||||
text-shadow: none;
|
||||
border-radius: 3px;
|
||||
margin: 0px;
|
||||
padding-left: 3px;
|
||||
padding-right: 18px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.openerp .oe_msg_content:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
.openerp .oe_mail .oe_mail_vote_count .oe_e{
|
||||
position: absolute;
|
||||
bottom: 1px;
|
||||
right: 2px;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.openerp .oe_chatter a {
|
||||
cursor: pointer;
|
||||
}
|
||||
/* c) Message action icons */
|
||||
|
||||
.openerp img.oe_mail_icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
.openerp .oe_mail .oe_msg.oe_msg_unread .oe_unread{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.openerp img.oe_mail_thumbnail {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin: 4px;
|
||||
.openerp .oe_mail .oe_msg.oe_msg_read .oe_read{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.openerp img.oe_mail_frame {
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icons{
|
||||
float: right;
|
||||
margin-top: 4px;
|
||||
margin-right: 8px;
|
||||
margin-left: 8px;
|
||||
height: 24px;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icons span{
|
||||
float:right;
|
||||
width:24px;
|
||||
height:24px;
|
||||
line-height:24px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-o-border-radius: 3px;
|
||||
-ms-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
||||
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
||||
-o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
||||
-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
||||
clip: rect(5px, 40px, 45px, 0px);
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icons a {
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
text-shadow: 0px 1px #AAA,0px -1px #AAA, -1px 0px #AAA, 1px 0px #AAA, 0px 3px 3px rgba(0,0,0,0.1);
|
||||
-webkit-transition: all 0.2s linear;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg:hover .oe_msg_icons a{
|
||||
opacity: 1;
|
||||
-webkit-transition: all 0.1s linear;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star:hover a{
|
||||
color: #FFF6C0;
|
||||
text-shadow: 0px 1px #FFA162,0px -1px #FFA162, -1px 0px #FFA162, 1px 0px #FFA162, 0px 3px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star.oe_starred a{
|
||||
color: #FFE41F;
|
||||
text-shadow: 0px 1px #DF6200,0px -1px #DF6200, -1px 0px #DF6200, 1px 0px #DF6200, 0px 3px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_reply:hover a{
|
||||
color: #1fc0ff;
|
||||
text-shadow: 0px 1px #184fc5,0px -1px #184fc5, -1px 0px #184fc5, 1px 0px #184fc5, 0px 3px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_read:hover a{
|
||||
color: #bbbaff;
|
||||
text-shadow: 0px 1px #7c7bad,0px -1px #7c7bad, -1px 0px #7c7bad, 1px 0px #7c7bad, 0px 3px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_unread:hover a{
|
||||
color: #c2ff00;
|
||||
text-shadow: 0px 1px #009441,0px -1px #009441, -1px 0px #009441, 1px 0px #009441, 0px 3px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_content textarea{
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
resize: vertical;
|
||||
|
||||
padding: 4px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg.oe_msg_composer_compact, .openerp .oe_mail .oe_msg.oe_msg_expandable{
|
||||
padding:4px;
|
||||
min-height:0px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg.oe_msg_composer_compact textarea{
|
||||
height: 24px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_invisible {
|
||||
/* ---------------- MESSAGE QUICK COMPOSER --------------- */
|
||||
|
||||
.openerp .oe_mail .oe_msg_composer .oe_msg_footer{
|
||||
padding-right:4px;
|
||||
padding-top: 2px;
|
||||
padding-bottom:6px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_attachments.oe_hidden,
|
||||
.openerp .oe_mail .oe_msg_images.oe_hidden{
|
||||
margin:0px;
|
||||
border: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Messages layout
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_mail_msg .oe_msg_title {
|
||||
margin: 0;
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
.openerp .oe_mail .oe_msg_attachments{
|
||||
margin-bottom: 4px;
|
||||
margin-right: 0px;
|
||||
font-size: 12px;
|
||||
border-radius: 2px;
|
||||
border: solid 1px rgba(124,123,173,0.14);
|
||||
}
|
||||
.openerp .oe_mail_msg .oe_msg_title a:link,
|
||||
.openerp .oe_mail_msg .oe_msg_title a:visited {
|
||||
color: #4C4C4C;
|
||||
.openerp .oe_mail .oe_msg_attachments .oe_attachment{
|
||||
padding: 2px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e{
|
||||
font-size: 23px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e:hover{
|
||||
text-decoration: none;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(odd){
|
||||
background:white;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(even){
|
||||
background: #F4F5FA;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_images {
|
||||
display: block;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer button{
|
||||
display: inline;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_attach{
|
||||
width: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_attach .oe_e{
|
||||
position: relative;
|
||||
top: -1px;
|
||||
left: -9px;
|
||||
}
|
||||
.openerp .oe_mail .oe_hidden_input_file, .openerp .oe_mail .oe_hidden_input_file form{
|
||||
display:inline;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_full{
|
||||
width:24px;
|
||||
overflow:hidden;
|
||||
float: right;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_full .oe_e{
|
||||
position: relative;
|
||||
top: -1px;
|
||||
left: -9px;
|
||||
}
|
||||
.openerp .oe_mail button.oe_attach, .openerp .oe_mail button.oe_full{
|
||||
background: transparent;
|
||||
color: #7C7BAD;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
text-shadow: none;
|
||||
}
|
||||
.openerp .oe_mail .oe_attach_label{
|
||||
color: #7C7BAD;
|
||||
margin-left: -3px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer .oe_attachment_file .oe_form_binary_file{
|
||||
display: inline-block;
|
||||
margin-left: -47px;
|
||||
height: 28px;
|
||||
width: 52px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.openerp .oe_mail .oe_mail_list_recipients{
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* ---------------- HIDDEN MESSAGES ------------------ */
|
||||
|
||||
.openerp .oe_mail .oe_msg_content.oe_msg_more_message{
|
||||
text-align: right;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_content.oe_msg_more_message .oe_separator{
|
||||
height: 0;
|
||||
border-bottom: dashed 1px #e6e6e6;
|
||||
margin-left: -4px;
|
||||
margin-right: 8px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: -9px;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more {
|
||||
background: white;
|
||||
margin-right: 280px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
text-decoration: none;
|
||||
color: #b4b4b4;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more:hover{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg .oe_msg_body {
|
||||
margin-bottom: .5em;
|
||||
text-align: justify;
|
||||
/* ---------------- FOLLOWERS ------------------ */
|
||||
|
||||
.openerp .oe_followers{
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-top: 5px;
|
||||
width: 160px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg .oe_msg_body pre {
|
||||
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
|
||||
margin: 0px;
|
||||
white-space: pre-wrap;
|
||||
/* a) THE FOLLOW BUTTON */
|
||||
|
||||
.openerp .oe_followers button.oe_follower{
|
||||
display: block;
|
||||
text-align: center;
|
||||
width:100%;
|
||||
}
|
||||
.openerp .oe_followers button.oe_follower.oe_following{
|
||||
background-color: #3465A4;
|
||||
background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.openerp .oe_followers button.oe_follower .oe_follow,
|
||||
.openerp .oe_followers button.oe_follower .oe_unfollow,
|
||||
.openerp .oe_followers button.oe_follower .oe_following{
|
||||
display: none;
|
||||
}
|
||||
/* a.1) when following, show 'following' */
|
||||
.openerp .oe_followers button.oe_follower.oe_following .oe_following{
|
||||
display: inline;
|
||||
}
|
||||
/* a.2) when following and hovering, show 'unfollow' */
|
||||
.openerp .oe_followers button.oe_follower.oe_following:hover .oe_following{
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_followers button.oe_follower.oe_following:hover .oe_unfollow{
|
||||
display: inline;
|
||||
}
|
||||
/* a.3) when not following show 'follow' */
|
||||
.openerp .oe_followers button.oe_follower.oe_notfollow .oe_follow{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Read more/less link */
|
||||
.openerp .oe_mail_msg span.oe_mail_reduce {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
.openerp .oe_followers .oe_subtype_list{
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Dropdown menu */
|
||||
|
||||
.openerp .oe_mail ul.oe_mail_thread_display ul.oe_mail_thread_display {
|
||||
position: relative;
|
||||
border-left: 1px #DDD dashed;
|
||||
/* b) THE FOLLOWERS */
|
||||
.openerp .oe_followers .oe_follower_title{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.openerp .oe_mail ul.oe_header {
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: -6px;
|
||||
z-index: 10;
|
||||
.openerp .oe_followers .oe_follower_title_box{
|
||||
margin-top: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail ul.oe_header a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.openerp .oe_mail ul.oe_header>li {
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Message footer */
|
||||
.openerp .oe_mail_msg .oe_msg_footer {
|
||||
color: #888;
|
||||
}
|
||||
.openerp .oe_mail_msg .oe_msg_footer li {
|
||||
float: left;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.openerp .oe_mail_msg .oe_msg_footer li:after {
|
||||
content: " · ";
|
||||
}
|
||||
.openerp .oe_mail_msg .oe_msg_footer li:last-child:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Attachments list */
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments {
|
||||
width: 100%;
|
||||
margin: .5em 0 0 0;
|
||||
padding: .5em 0;
|
||||
list-style-position: inside;
|
||||
}
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments.oe_hidden {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments li {
|
||||
float: none;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: square;
|
||||
}
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process {
|
||||
.openerp .oe_followers .oe_invite{
|
||||
float: right;
|
||||
width: 200px;
|
||||
height: 16px;
|
||||
}
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process div {
|
||||
float: left;
|
||||
width: 38px;
|
||||
height: 16px;
|
||||
margin-right: 2px;
|
||||
background: #66FF66;
|
||||
.openerp .oe_followers .oe_partner {
|
||||
height: 32px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process span {
|
||||
color: #aaaaaa;
|
||||
position: absolute;
|
||||
.openerp .oe_followers .oe_partner img{
|
||||
width: 32px;
|
||||
margin-right:4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Topbar button
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ---------------- MESSAGES BODY ------------------ */
|
||||
.openerp .oe_mail .oe_msg_content .oe_blockquote,
|
||||
.openerp .oe_mail .oe_msg_content blockquote {
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
border: solid 1px rgba(124,123,173,0.14);
|
||||
}
|
||||
|
||||
.openerp .oe_topbar .oe_topbar_compose_full_email {
|
||||
float: right;
|
||||
margin: 3px 25px 0 0;
|
||||
}
|
||||
/* ----------- FORM INTEGRATION ------------ */
|
||||
|
||||
.openerp .oe_record_thread{
|
||||
display: block;
|
||||
margin-right: 180px;
|
||||
}
|
||||
|
||||
/* ----------- INBOX INTEGRATION ----------- */
|
||||
|
||||
.openerp .oe_mail_wall .oe_mail{
|
||||
margin: 16px;
|
||||
width: 720px;
|
||||
}
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
/* ------------------------------ */
|
||||
/* Compose Message */
|
||||
/* ------------------------------ */
|
||||
|
||||
.openerp .oe_msg_content .oe_mail_compose_message_footer {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.openerp .oe_msg_content .oe_mail_compose_message_footer button.oe_mail_compose_message_button_send {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_mail_compose_textarea
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_mail_compose_textarea .oe_mail_post_header,
|
||||
.openerp .oe_mail .oe_mail_compose_textarea .oe_mail_post_footer,
|
||||
{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_mail_compose_textarea a.oe_cancel {
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: -8px;
|
||||
}
|
||||
.openerp .oe_mail .oe_mail_compose_textarea a.oe_cancel:first-of-type {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_mail_compose_textarea button.oe_full {
|
||||
float: right;
|
||||
position: relative;
|
||||
right: -10px;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* mail.compose.message : list_recipients
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_mail .oe_mail_list_recipients {
|
||||
display: inline;
|
||||
}
|
||||
.openerp .oe_mail .oe_mail_list_recipients .oe_all_follower {
|
||||
color: blue;
|
||||
}
|
||||
.openerp .oe_mail .oe_mail_list_recipients .oe_partner_follower a {
|
||||
color: red;
|
||||
}
|
||||
.openerp .oe_mail .oe_mail_list_recipients .oe_hidden,
|
||||
.openerp .oe_mail .oe_mail_list_recipients .oe_more_hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* mail.compose.message : attachment
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_mail .oe_attachment_file {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_attachment_file .oe_add {
|
||||
float: left;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
left: +2px;
|
||||
top: +7px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* attachment button: override of openerp values */
|
||||
.openerp .oe_mail .oe_attachment_file .oe_add button,
|
||||
.openerp .oe_mail .oe_attachment_file .oe_add input.oe_insert_file {
|
||||
position: absolute;
|
||||
bottom: +0px;
|
||||
left: +0px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
.openerp .oe_mail .oe_attachment_file .oe_add input.oe_insert_file {
|
||||
z-index:2;
|
||||
width: 300px;
|
||||
left: -100px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: transparent;
|
||||
}
|
||||
.openerp .oe_mail .oe_attachment_file .oe_add button span {
|
||||
position: relative;
|
||||
bottom: +4px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_msg_attachments input {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_mail_compose_attachment_list {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* mail.compose.message
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
|
||||
/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */
|
||||
.openerp .oe_mail.oe_semantic_html_override .oe_mail_compose_textarea textarea.field_text,
|
||||
.openerp .oe_mail div.oe_mail_compose_message_body textarea.field_text {
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
height: auto;
|
||||
padding: 4px;
|
||||
font-size: 12px;
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
/* not top textarea */
|
||||
.openerp .oe_mail.oe_semantic_html_override .oe_semantic_html_override .oe_mail_compose_textarea textarea.field_text {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
/* form_view: delete white background */
|
||||
.openerp .oe_msg_content div.oe_formview {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.openerp .oe_msg_content div.oe_form_nosheet {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.openerp .oe_msg_content table.oe_form_group {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.openerp .oe_msg_content table.oe_form_field,
|
||||
.openerp .oe_msg_content div.oe_form_field {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.openerp .oe_msg_content td.oe_form_group_cell {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
/* subject: change width */
|
||||
.openerp .oe_msg_content .oe_form .oe_form_field input[type='text'] {
|
||||
width: 472px;
|
||||
}
|
||||
|
||||
/* body_html: cleditor */
|
||||
.openerp .oe_msg_content div.cleditorMain {
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
/* destination_partner_ids */
|
||||
.openerp .oe_msg_content div.text-core {
|
||||
height: 22px !important;
|
||||
width: 472px;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -24,10 +24,9 @@ openerp_mail_followers = function(session, mail) {
|
|||
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.options.image = this.node.attrs.image || 'image_small';
|
||||
this.options.title = this.node.attrs.title || 'Followers';
|
||||
this.options.comment = this.node.attrs.help || false;
|
||||
this.options.displayed_nb = this.node.attrs.displayed_nb || 10;
|
||||
this.image = this.node.attrs.image || 'image_small';
|
||||
this.comment = this.node.attrs.help || false;
|
||||
this.displayed_nb = this.node.attrs.displayed_nb || 10;
|
||||
this.ds_model = new session.web.DataSetSearch(this, this.view.model);
|
||||
this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
|
||||
this.ds_users = new session.web.DataSetSearch(this, 'res.users');
|
||||
|
@ -61,7 +60,7 @@ openerp_mail_followers = function(session, mail) {
|
|||
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);
|
||||
this.$el.on('click', '.oe_subtype_list input', self.do_update_subscription);
|
||||
// event: click on 'invite' button, that opens the invite wizard
|
||||
this.$('.oe_invite').on('click', function (event) {
|
||||
action = {
|
||||
|
@ -115,20 +114,24 @@ openerp_mail_followers = function(session, mail) {
|
|||
self.message_is_follower = (_.indexOf(self.get('value'), pid) != -1);
|
||||
}).pipe(self.proxy('display_generic'));
|
||||
},
|
||||
|
||||
_format_followers: function(count){
|
||||
// TDE note: why redefining _t ?
|
||||
function _t(str) { return str; }
|
||||
var str = '';
|
||||
if(count <= 0){
|
||||
str = _t('No followers');
|
||||
}else if(count === 1){
|
||||
str = _t('One follower');
|
||||
}else{
|
||||
str = ''+count+' '+_t('followers');
|
||||
}
|
||||
return str;
|
||||
},
|
||||
/* Display generic info about follower, for people not having access to res_partner */
|
||||
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.get('value').length-1) + ' other)';
|
||||
}
|
||||
else {
|
||||
content += ' (' + this.get('value').length + ')'
|
||||
}
|
||||
this.$('div.oe_mail_recthread_followers h4').html(content);
|
||||
var node_user_list = this.$('.oe_follower_list').empty();
|
||||
this.$('.oe_follower_title').html(this._format_followers(this.get('value').length));
|
||||
},
|
||||
|
||||
/** Display the followers */
|
||||
|
@ -137,16 +140,17 @@ openerp_mail_followers = function(session, mail) {
|
|||
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 + ')');
|
||||
var node_user_list = this.$('.oe_follower_list').empty();
|
||||
this.$('.oe_follower_title').html(this._format_followers(records.length));
|
||||
// truncate number of displayed followers
|
||||
truncated = records.splice(0, this.options.displayed_nb);
|
||||
truncated = records.splice(0, this.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);
|
||||
});
|
||||
// FVA note: be sure it is correctly translated
|
||||
if (truncated.length < records.length) {
|
||||
$('<li>And ' + (records.length - truncated.length) + ' more.</li>').appendTo(node_user_list);
|
||||
$('<div class="oe_partner">And ' + (records.length - truncated.length) + ' more.</div>').appendTo(node_user_list);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -172,7 +176,7 @@ openerp_mail_followers = function(session, mail) {
|
|||
|
||||
/** Fetch subtypes, only if current user is follower */
|
||||
fetch_subtypes: function () {
|
||||
var subtype_list_ul = this.$('.oe_subtypes').empty();
|
||||
var subtype_list_ul = this.$('.oe_subtype_list').empty();
|
||||
if (! this.message_is_follower) return;
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).pipe(this.proxy('display_subtypes'));
|
||||
|
@ -181,13 +185,12 @@ openerp_mail_followers = function(session, mail) {
|
|||
/** 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].message_subtype_data;
|
||||
|
||||
var subtype_list_ul = this.$('.oe_subtype_list');
|
||||
var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data;
|
||||
_(records).each(function (record, record_name) {
|
||||
record.name = record_name;
|
||||
record.followed = record.followed || undefined;
|
||||
$(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo( self.$('ul.oe_subtypes') );
|
||||
$(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo( self.$('.oe_subtype_list') );
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -210,7 +213,7 @@ openerp_mail_followers = function(session, mail) {
|
|||
var self = this;
|
||||
|
||||
var checklist = new Array();
|
||||
_(this.$('.oe_mail_recthread_actions input[type="checkbox"]')).each(function (record) {
|
||||
_(this.$('.oe_actions input[type="checkbox"]')).each(function (record) {
|
||||
if ($(record).is(':checked')) {
|
||||
checklist.push(parseInt($(record).data('id')));
|
||||
}
|
||||
|
|
|
@ -1,33 +1,46 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
|
||||
<!-- this template contains the mail widget and is used to namespace the css -->
|
||||
<t t-name="mail.Widget">
|
||||
<div class="oe_mail">
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
mail.compose_message template
|
||||
This template holds the composition form to write a note or send
|
||||
an e-mail. It contains by default a textarea, that will be replaced
|
||||
by another composition form in the main wall composition form, or
|
||||
for main thread composition form in document form view.
|
||||
|
||||
mail.compose_message.compact template
|
||||
This template holds the composition form to write a message, this box is converted into
|
||||
mail.compose_message when focus on textarea
|
||||
-->
|
||||
<t t-name="mail.compose_message">
|
||||
<div class="oe_mail_compose_textarea">
|
||||
<img class="oe_mail_icon oe_mail_frame oe_left" alt="User img"/>
|
||||
<div class="oe_msg_content">
|
||||
<!-- contains the composition form -->
|
||||
<!-- default content: old basic textarea -->
|
||||
<div class="oe_mail_post_header">
|
||||
<div t-if="widget.show_composer" t-attf-class="oe_msg oe_msg_composer #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
|
||||
<div class="oe_msg_left">
|
||||
<img class="oe_msg_icon" alt="User img" t-attf-src="#{widget.avatar}"/>
|
||||
</div>
|
||||
<div class="oe_msg_center">
|
||||
<div class="oe_msg_content">
|
||||
<t t-call="mail.thread.list_recipients"/>
|
||||
<a class="oe_cancel oe_e">X</a>
|
||||
<textarea class="field_text"></textarea>
|
||||
</div>
|
||||
<textarea class="field_text" placeholder="Add your comment here..."/>
|
||||
<div class="oe_mail_post_footer">
|
||||
<div class="oe_mail_compose_attachment_list"/>
|
||||
<button class="oe_full">Full mail message</button>
|
||||
<button class="oe_post">Post message</button>
|
||||
<div class="oe_msg_footer">
|
||||
<div class="oe_msg_attachment_list"></div>
|
||||
<button class="oe_post">Post</button>
|
||||
<t t-call="mail.compose_message.add_attachment"/>
|
||||
<!--<a class="oe_cancel oe_e">X</a>-->
|
||||
<button class="oe_full"><span class='oe_e'>&ograve</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_clear"/>
|
||||
</div>
|
||||
<div t-if="widget.show_compact_message and !widget.show_composer" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
|
||||
<textarea class="field_text oe_compact" placeholder="Write a reply..."/>
|
||||
</div>
|
||||
<span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
|
@ -35,19 +48,20 @@
|
|||
Small template to be inserted in the composition for add attachments
|
||||
-->
|
||||
<t t-name="mail.compose_message.add_attachment">
|
||||
<div class="oe_attachment_file">
|
||||
<div class="oe_add">
|
||||
<span class="oe_attachment_file">
|
||||
<span class="oe_add">
|
||||
<!-- uploader of file -->
|
||||
<button><span class="oe_e">p</span></button>
|
||||
<button class="oe_attach"><span class="oe_e">'</span></button>
|
||||
<span class='oe_attach_label'>File</span>
|
||||
<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="mail.message"/>
|
||||
<input type="hidden" name="model" value="mail.compose.message"/>
|
||||
<input type="hidden" name="id" value="0"/>
|
||||
<input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
|
@ -55,25 +69,33 @@
|
|||
Template used to display attachments in a mail.message
|
||||
-->
|
||||
<t t-name="mail.thread.message.attachments">
|
||||
<ul t-attf-class="oe_msg_attachments #{widget.datasets.attachment_ids[0] and widget.options.thread.show_attachment_link?'':'oe_hidden'}">
|
||||
<t t-foreach="widget.datasets.attachment_ids" t-as="attachment">
|
||||
<li>
|
||||
<span t-if="(attachment.upload or attachment.percent_loaded<100)" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-attf-name="{attachment.name || attachment.filename}">
|
||||
<span class="oe_msg_attachments">
|
||||
<t t-foreach="widget.attachment_ids" t-as="attachment" t-if="!attachment.is_image">
|
||||
<div class="oe_attachment">
|
||||
<span t-if="(attachment.upload and attachment.percent_loaded<100)" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-attf-name="{attachment.name || attachment.filename}">
|
||||
<div class="oe_upload_in_process">
|
||||
<span>...Upload in progress...</span>
|
||||
</div>
|
||||
<t t-raw="attachment.name || attachment.filename"/>
|
||||
</span>
|
||||
<a t-if="(!attachment.upload or attachment.percent_loaded>=100)" t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
|
||||
<t t-raw="attachment.name || attachment.filename"/>
|
||||
</a>
|
||||
<t t-if="widget.options.thread.show_attachment_delete and (!attachment.upload or attachment.percent_loaded>=100)">
|
||||
<a class="oe_right oe_mail_attachment_delete" title="Delete this attachment" t-attf-data-id="{attachment.id}">x</a>
|
||||
<t t-if="(!attachment.upload or attachment.percent_loaded>=100)">
|
||||
<a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
|
||||
<t t-raw="attachment.name || attachment.filename"/>
|
||||
</a>
|
||||
</t>
|
||||
|
||||
</li>
|
||||
<t t-if="(widget.show_delete_attachment and (!attachment.upload or attachment.percent_loaded>=100))">
|
||||
<a class="oe_right oe_mail_attachment_delete oe_e" title="Delete this attachment" t-attf-data-id="{attachment.id}">[</a>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</ul>
|
||||
</span>
|
||||
<span class="oe_msg_images">
|
||||
<t t-foreach="widget.attachment_ids" t-as="attachment" t-if="attachment.is_image">
|
||||
<a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
|
||||
<img t-if="attachment.is_image" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-att-src="attachment.url"/>
|
||||
</a>
|
||||
</t>
|
||||
</span>
|
||||
</t>
|
||||
|
||||
<t t-name="mail.thread.message.private">
|
||||
|
@ -89,14 +111,14 @@
|
|||
-->
|
||||
<t t-name="mail.thread.list_recipients">
|
||||
<div class="oe_mail_list_recipients">
|
||||
Post to:
|
||||
<span t-if="!widget.datasets.is_private" class="oe_all_follower">All Followers</span>
|
||||
<t t-if="!widget.datasets.is_private and widget.datasets.partner_ids.length"> and </t>
|
||||
To:
|
||||
<span t-if="!widget.is_private" class="oe_all_follower">Everyone</span>
|
||||
<t t-if="!widget.is_private and widget.partner_ids.length"> and </t>
|
||||
<t t-set="inc" t-value="0"/>
|
||||
<t t-if="widget.datasets.partner_ids.length" t-foreach="widget.datasets.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 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>
|
||||
<t t-if="widget.datasets.partner_ids.length>=3">
|
||||
<span class="oe_more">, <a><t t-raw="widget.datasets.partner_ids.length-3"/> others...</a></span>
|
||||
<t t-if="widget.partner_ids.length>=3">
|
||||
<span class="oe_more">, <a><t t-raw="widget.partner_ids.length-3"/> others...</a></span>
|
||||
<a class="oe_more_hidden"><<<</a>
|
||||
</t>
|
||||
</div>
|
||||
|
@ -131,122 +153,95 @@
|
|||
<button type="button" class="oe_write_full oe_highlight">
|
||||
Compose a new message
|
||||
</button>
|
||||
<button type="button" class="oe_write_onwall" help="Your followers can read this message">
|
||||
Write to your followers
|
||||
</button>
|
||||
<span class='oe_alternative'>
|
||||
or
|
||||
<a href='#' class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write to your followers</a>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- placeholder for the wall threads -->
|
||||
<div class="oe_mail_wall_threads"/>
|
||||
<div class="oe_mail_wall_aside">
|
||||
<!-- contains currently nothing -->
|
||||
</div>
|
||||
<div class="oe_mail-placeholder"></div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
display message on the wall when there are no message
|
||||
-->
|
||||
<li t-name="mail.wall_no_message" class="oe_wall_no_message">
|
||||
You have no messages
|
||||
</li>
|
||||
<t t-name="mail.wall_no_message">
|
||||
<div class="oe_wall_no_message">You have no messages</div>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
record_thread main template
|
||||
Template used to display the communication history in documents
|
||||
form view.
|
||||
-->
|
||||
<div t-name="mail.record_thread" class="oe_mail_record_wall">
|
||||
<!-- <h4>History and Comments</h4> -->
|
||||
<ul class="oe_mail_wall_threads">
|
||||
<!-- contains the document thread -->
|
||||
</ul>
|
||||
<div t-name="mail.record_thread" class="oe_record_thread">
|
||||
<div class="oe_mail-placeholder">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
thread template
|
||||
This template holds a thread of comments. It begins with an actions
|
||||
container, holding the composition form. Then come the various
|
||||
messages. Then comes the 'more' button.
|
||||
-->
|
||||
<div t-name="mail.thread" class="oe_mail oe_mail_thread oe_semantic_html_override">
|
||||
<div class="oe_mail_thread_action">
|
||||
<!-- contains the composition box (form + image) -->
|
||||
</div>
|
||||
<ul class="oe_mail_thread_display">
|
||||
<!-- contains the threads -->
|
||||
</ul>
|
||||
</div>
|
||||
<t t-name="mail.thread">
|
||||
<div t-attf-class="oe_thread #{widget.root?'oe_root_thread':''}"/>
|
||||
</t>
|
||||
|
||||
<!-- default layout -->
|
||||
<li t-name="mail.thread.message" t-attf-class="oe_mail oe_mail_thread_msg #{widget.datasets.to_read ?'oe_mail_unread':'oe_mail_read'}">
|
||||
<div t-attf-class="oe_msg_#{widget.datasets.type} oe_semantic_html_override">
|
||||
<!-- message actions (read/unread, reply, delete...) -->
|
||||
<ul class="oe_header">
|
||||
<li class="placeholder-mail-vote"><t t-call="mail.thread.message.vote"/></li>
|
||||
<li class="placeholder-mail-star"><t t-call="mail.thread.message.star"/></li>
|
||||
<li t-if="widget.datasets.show_read_unread" title="Read" class="oe_read"><a class="oe_read oe_e">W</a></li>
|
||||
<li t-if="widget.datasets.show_read_unread" title="Set back to unread" class="oe_unread"><a class="oe_unread oe_e">h</a></li>
|
||||
<li title="Quick reply" t-if="widget.datasets.show_reply"><a class="oe_reply oe_e">)</a></li>
|
||||
<t t-if="(widget.datasets.is_author and widget.options.message.show_dd_delete) or widget.datasets.type == 'email'">
|
||||
<li>
|
||||
<span class="oe_dropdown_toggle">
|
||||
<a class="oe_e" title="More options">í</a>
|
||||
<ul class="oe_dropdown_menu">
|
||||
<li t-if="widget.datasets.is_author and widget.options.message.show_dd_delete"><a class="oe_msg_delete">Delete</a></li>
|
||||
<li t-if="widget.datasets.type == 'email'"><a class="oe_msg_details" t-attf-href="#model=mail.message&id=#{widget.datasets.id}" >Details</a></li>
|
||||
</ul>
|
||||
</span>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
<t t-name="mail.thread.message">
|
||||
<div t-attf-class="oe_msg #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_#{widget.type} oe_msg_#{widget.to_read?'unread':'read'}">
|
||||
|
||||
<a t-attf-href="#model=res.partner&id=#{widget.datasets.author_id[0]}" t-att-title="widget.datasets.author_id[1]">
|
||||
<img class="oe_mail_icon oe_mail_frame oe_left" t-att-src="widget.datasets.avatar"/>
|
||||
</a>
|
||||
<div class='oe_msg_left'>
|
||||
<a t-attf-href="#model=res.partner&id=#{widget.author_id[0]}" t-att-title="widget.author_id[1]">
|
||||
<img class="oe_msg_icon" t-att-src="widget.avatar"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="oe_msg_content">
|
||||
<div class="oe_msg_center">
|
||||
<div class='oe_msg_icons'>
|
||||
<span class='oe_read' t-if="widget.show_read_unread_button"><a title="Read" class="oe_e">X</a></span>
|
||||
<span class='oe_unread' t-if="widget.show_read_unread_button"><a title="Set back to unread" class="oe_e">v</a></span>
|
||||
<span class='oe_reply' t-if="widget.show_reply_button"><a title="Reply" class="oe_e">(</a></span>
|
||||
<span t-attf-class="oe_star #{widget.is_favorite?'oe_starred':''}"><a title="Add To Favorites" class="oe_e">7</a></span>
|
||||
</div>
|
||||
<!-- message itself -->
|
||||
<div class="oe_mail_msg">
|
||||
<h1 t-if="widget.datasets.subject" class="oe_msg_title">
|
||||
<t t-raw="widget.datasets.subject"/>
|
||||
<div class="oe_msg_content">
|
||||
<h1 t-if="widget.subject and !widget.thread_level" class="oe_msg_title">
|
||||
<t t-raw="widget.subject"/>
|
||||
</h1>
|
||||
<ul class="oe_msg_footer">
|
||||
<li t-if="widget.datasets.author_id"><a t-attf-href="#model=res.partner&id=#{widget.datasets.author_id[0]}"><t t-raw="widget.datasets.author_id[1]"/></a></li>
|
||||
<li><span t-att-title="widget.datasets.date"><t t-raw="widget.datasets.timerelative"/></span></li>
|
||||
<li t-if="widget.datasets.attachment_ids.length > 0">
|
||||
<a class="oe_msg_view_attachments">
|
||||
<t t-if="widget.datasets.attachment_ids.length == 1">1 Attachment</t>
|
||||
<t t-if="widget.datasets.attachment_ids.length > 1"><t t-raw="widget.datasets.attachment_ids.length"/> Attachments</t>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="oe_clear"/>
|
||||
<div class="oe_msg_body">
|
||||
<t t-if="widget.options.message.show_record_name and widget.datasets.record_name and (!widget.datasets.subject) and !widget.options.thread.thread_level and !widget.options.thread.display_on_thread[0] and widget.datasets.model!='res.partner'">
|
||||
<a class="oe_mail_action_model" t-attf-href="#model=#{widget.datasets.model}&id=#{widget.res_id}"><t t-raw="widget.datasets.record_name"/></a>
|
||||
<t t-if="widget.options.show_record_name and widget.record_name and (!widget.subject) and !widget.options.thread_level and !widget.options.display_on_thread[0] and widget.model!='res.partner'">
|
||||
<a class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&id=#{widget.res_id}"><t t-raw="widget.record_name"/></a>
|
||||
</t>
|
||||
<t t-raw="widget.datasets.body"/>
|
||||
<t t-raw="widget.body"/>
|
||||
</div>
|
||||
<t t-if="widget.datasets.attachment_ids.length > 0">
|
||||
<div class="oe_clear"></div>
|
||||
<t t-if="widget.attachment_ids.length > 0">
|
||||
<t t-call="mail.thread.message.attachments"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="oe_msg_footer">
|
||||
<a t-if="widget.author_id" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-raw="widget.author_id[1]"/></a>
|
||||
<span class='oe_subtle'>•</span>
|
||||
<span t-att-title="widget.date"><t t-raw="widget.timerelative"/></span>
|
||||
<a t-if="widget.attachment_ids.length > 0" class="oe_mail_msg_view_attachments">
|
||||
<t t-if="widget.attachment_ids.length == 1">1 Attachment</t>
|
||||
<t t-if="widget.attachment_ids.length > 1"><t t-raw="widget.attachment_ids.length"/> Attachments</t>
|
||||
</a>
|
||||
<span class='oe_subtle'>•</span>
|
||||
<t t-call="mail.thread.message.vote"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_thread_placeholder"></div>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<!-- expandable message layout -->
|
||||
<li t-name="mail.thread.expandable" class="oe_mail oe_mail_thread_msg oe_mail_unread">
|
||||
<div t-attf-class="oe_msg_#{widget.datasets.type} oe_semantic_html_override">
|
||||
<t t-name="mail.thread.expandable">
|
||||
<div t-attf-class="oe_msg oe_msg_#{widget.type} #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_unread">
|
||||
<div class="oe_msg_content oe_msg_more_message">
|
||||
<a class="oe_mail_fetch_more">Load more messages <span t-if="widget.datasets.nb_messages>0">(<t t-raw="widget.datasets.nb_messages"/> messages not display)</span>...</a>
|
||||
<div class='oe_separator'></div>
|
||||
<a t-if="widget.nb_messages === 1" class="oe_msg_fetch_more">show one more message</a>
|
||||
<a t-if="widget.nb_messages !== 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
mail.compose_message.button_top_bar
|
||||
|
@ -254,7 +249,7 @@
|
|||
-->
|
||||
<t t-name="mail.compose_message.button_top_bar">
|
||||
<div class="oe_topbar_compose_full_email">
|
||||
<button class="oe_button oe_highlight">Write an email</button>
|
||||
<!-- <button class="oe_button oe_highlight">Write an email</button> -->
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
@ -262,33 +257,18 @@
|
|||
Template used to display Like/Unlike in a mail.message
|
||||
-->
|
||||
<span t-name="mail.thread.message.vote">
|
||||
<span class="oe_left oe_mail_vote_count">
|
||||
<t t-if='widget.datasets.has_voted'>
|
||||
You
|
||||
</t>
|
||||
<t t-if='(widget.datasets.vote_user_ids.length-(widget.datasets.has_voted?1:0)) > 0'>
|
||||
<t t-if='widget.datasets.has_voted'> and </t>
|
||||
<t t-esc="widget.datasets.vote_user_ids.length"/> people
|
||||
</t>
|
||||
<t t-if='widget.datasets.vote_user_ids.length > 0'>
|
||||
agree
|
||||
</t>
|
||||
|
||||
<span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
|
||||
<t t-raw='widget.vote_nb' />
|
||||
<span class='oe_e'>8</span>
|
||||
</span>
|
||||
<button t-attf-class="oe_msg_vote oe_tag">
|
||||
<span>
|
||||
<t t-if="!widget.has_voted">Agree</t>
|
||||
<t t-if="widget.has_voted">Undo</t>
|
||||
</span>
|
||||
</button>
|
||||
<a href='#' t-attf-class="oe_msg_vote">
|
||||
<t t-if="!widget.has_voted">like</t>
|
||||
<t t-if="widget.has_voted">unlike</t>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<!-- mail.thread.message.star
|
||||
Template used to display stared/unstared message in a mail.message
|
||||
-->
|
||||
<span t-name="mail.thread.message.star">
|
||||
<span class="oe_left">
|
||||
<button t-attf-class="oe_mail_starbox oe_tag #{widget.datasets.is_favorite?'oe_stared':''}">*</button>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
</template>
|
||||
|
|
|
@ -5,51 +5,45 @@
|
|||
followers main template
|
||||
Template used to display the followers, the actions and the subtypes in a record.
|
||||
-->
|
||||
<div t-name="mail.followers" class="oe_mail_recthread_aside oe_semantic_html_override">
|
||||
<div class="oe_mail_recthread_actions">
|
||||
<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>
|
||||
<span class="oe_following">Following</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_recthread_subtypes">
|
||||
<ul class="oe_subtypes"></ul>
|
||||
</div>
|
||||
<div t-name="mail.followers" class="oe_followers">
|
||||
<div class="oe_actions">
|
||||
<button type="button" class="oe_follower oe_notfollow">
|
||||
<span class="oe_follow">Follow</span>
|
||||
<span class="oe_unfollow">Unfollow</span>
|
||||
<span class="oe_following">Following</span>
|
||||
</button>
|
||||
<div class="oe_subtype_list"></div>
|
||||
</div>
|
||||
<t t-if="widget.options.comment">
|
||||
<h5 class="oe_grey"><t t-raw="widget.options.comment"/></h5>
|
||||
<t t-if="widget.comment">
|
||||
<h5 class="oe_comment"><t t-raw="widget.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">
|
||||
<h4><t t-raw="widget.options.title"/></h4>
|
||||
</t>
|
||||
<ul class="oe_mail_followers_display"></ul>
|
||||
<div class='oe_follower_title_box'>
|
||||
<h4 class='oe_follower_title'>Followers</h4>
|
||||
<a href='#' class="oe_invite">Invite others</a>
|
||||
</div>
|
||||
<div class="oe_follower_list"></div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
followers.partner template
|
||||
Template used to display a partner following the record
|
||||
-->
|
||||
<li t-name="mail.followers.partner">
|
||||
<div t-name="mail.followers.partner" class='oe_partner'>
|
||||
<img class="oe_mail_thumbnail oe_mail_frame" t-attf-src="{record.avatar_url}"/>
|
||||
<a t-attf-href="#model=res.partner&id=#{record.id}"><t t-raw="record.name"/></a>
|
||||
</li>\
|
||||
</div>
|
||||
|
||||
<!--
|
||||
followers.subtype template
|
||||
Template used to display message subtypes of a follower subscription
|
||||
-->
|
||||
<li t-name="mail.followers.subtype">
|
||||
<table width="50%">
|
||||
<t t-name="mail.followers.subtype">
|
||||
<table class='oe_subtype'>
|
||||
<tr>
|
||||
<td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-raw="record.name"/></label></td>
|
||||
<td width="10%"><input type="checkbox" t-att-checked="record.followed" t-att-id="'input_mail_followers_subtype_'+record.id" t-att-data-id="record.id" t-att-name="record.name" class="oe_msg_subtype_check"/></td>
|
||||
<td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-raw="record.name"/></label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
</template>
|
||||
|
|
|
@ -94,6 +94,8 @@ class TestMailMockups(common.TransactionCase):
|
|||
self._build_email_kwargs_list = []
|
||||
|
||||
def _mock_build_email(self, *args, **kwargs):
|
||||
""" Mock build_email to be able to test its values. Store them into
|
||||
some internal variable for latter processing. """
|
||||
self._build_email_args_list.append(args)
|
||||
self._build_email_kwargs_list.append(kwargs)
|
||||
return self._build_email(*args, **kwargs)
|
||||
|
@ -123,6 +125,7 @@ class test_mail(TestMailMockups):
|
|||
|
||||
def setUp(self):
|
||||
super(test_mail, self).setUp()
|
||||
cr, uid = self.cr, self.uid
|
||||
self.ir_model = self.registry('ir.model')
|
||||
self.mail_alias = self.registry('mail.alias')
|
||||
self.mail_thread = self.registry('mail.thread')
|
||||
|
@ -135,22 +138,27 @@ class test_mail(TestMailMockups):
|
|||
self.res_users = self.registry('res.users')
|
||||
self.res_partner = self.registry('res.partner')
|
||||
|
||||
# Find Employee group
|
||||
group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
|
||||
group_employee_id = group_employee_ref and group_employee_ref[1] or False
|
||||
# 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)
|
||||
self.user_raoul_id = self.res_users.create(cr, uid,
|
||||
{'name': 'Raoul Grosbedon', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [group_employee_id])]})
|
||||
self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
|
||||
self.user_admin = self.res_users.browse(cr, uid, 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
|
||||
self.registry('mail.mail').send_get_mail_body = self._mock_send_get_mail_body
|
||||
|
||||
# groups@.. will cause the creation of new mail groups
|
||||
self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model', '=', 'mail.group')])[0]
|
||||
self.mail_alias.create(self.cr, self.uid, {'alias_name': 'groups',
|
||||
self.mail_group_model_id = self.ir_model.search(cr, uid, [('model', '=', 'mail.group')])[0]
|
||||
self.mail_alias.create(cr, uid, {'alias_name': 'groups',
|
||||
'alias_model_id': self.mail_group_model_id})
|
||||
# create a 'pigs' group that will be used through the various tests
|
||||
self.group_pigs_id = self.mail_group.create(self.cr, self.uid,
|
||||
self.group_pigs_id = self.mail_group.create(cr, uid,
|
||||
{'name': 'Pigs', 'description': 'Fans of Pigs, unite !'})
|
||||
self.group_pigs = self.mail_group.browse(self.cr, self.uid, self.group_pigs_id)
|
||||
self.group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
|
||||
|
||||
def tearDown(self):
|
||||
# Remove mocks
|
||||
|
@ -159,7 +167,7 @@ class test_mail(TestMailMockups):
|
|||
|
||||
def test_00_message_process(self):
|
||||
""" Testing incoming emails processing. """
|
||||
cr, uid = self.cr, self.uid
|
||||
cr, uid, user_raoul = self.cr, self.uid, self.user_raoul
|
||||
# Incoming mail creates a new mail_group "frogs"
|
||||
self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', 'frogs')]), [])
|
||||
mail_frogs = MAIL_TEMPLATE.format(to='groups@example.com, other@gmail.com', subject='frogs', extra='')
|
||||
|
@ -197,6 +205,26 @@ 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')
|
||||
|
||||
# Do: post a new message, with a known partner
|
||||
test_msg_id = '<deadcafe.1337-2@smtp.agrolait.com>'
|
||||
TEMPLATE_MOD = MAIL_TEMPLATE_PLAINTEXT.replace('Sylvie Lelitre <sylvie.lelitre@agrolait.com>', user_raoul.email)
|
||||
mail_new = TEMPLATE_MOD.format(to='Friendly Frogs <group+frogs@example.com>', subject='extra news', extra='', msg_id=test_msg_id)
|
||||
self.mail_thread.message_process(cr, uid, None, mail_new)
|
||||
new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
|
||||
# Test: author_id set, not email_from
|
||||
self.assertEqual(new_mail.author_id, user_raoul.partner_id, 'message process wrong author found')
|
||||
self.assertFalse(new_mail.email_from, 'message process should not set the email_from when an author is found')
|
||||
|
||||
# Do: post a new message, with a unknown partner
|
||||
test_msg_id = '<deadcafe.1337-3@smtp.agrolait.com>'
|
||||
TEMPLATE_MOD = MAIL_TEMPLATE_PLAINTEXT.replace('Sylvie Lelitre <sylvie.lelitre@agrolait.com>', '_abcd_')
|
||||
mail_new = TEMPLATE_MOD.format(to='Friendly Frogs <group+frogs@example.com>', subject='super news', extra='', msg_id=test_msg_id)
|
||||
self.mail_thread.message_process(cr, uid, None, mail_new)
|
||||
new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
|
||||
# Test: author_id set, not email_from
|
||||
self.assertFalse(new_mail.author_id, 'message process shnould not have found a partner for _abcd_ email address')
|
||||
self.assertIn('_abcd_', new_mail.email_from, 'message process should set en email_from when not finding a partner_id')
|
||||
|
||||
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,
|
||||
|
@ -260,7 +288,7 @@ class test_mail(TestMailMockups):
|
|||
""" Tests designed for the subscriber API as well as message subtypes """
|
||||
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_id = self.user_raoul_id
|
||||
user_raoul = self.res_users.browse(cr, uid, user_raoul_id)
|
||||
# Data: message subtypes
|
||||
self.mail_message_subtype.create(cr, uid, {'name': 'mt_mg_def', 'default': True, 'res_model': 'mail.group'})
|
||||
|
@ -311,7 +339,25 @@ class test_mail(TestMailMockups):
|
|||
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):
|
||||
def test_20_message_quote_context(self):
|
||||
""" Tests designed for message_post. """
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
|
||||
msg1_id = self.mail_message.create(cr, uid, {'body': 'Thread header about Zap Brannigan', 'subject': 'My subject'})
|
||||
msg2_id = self.mail_message.create(cr, uid, {'body': 'First answer, should not be displayed', 'subject': 'Re: My subject', 'parent_id': msg1_id})
|
||||
msg3_id = self.mail_message.create(cr, uid, {'body': 'Second answer', 'subject': 'Re: My subject', 'parent_id': msg1_id})
|
||||
msg4_id = self.mail_message.create(cr, uid, {'body': 'Third answer', 'subject': 'Re: My subject', 'parent_id': msg1_id})
|
||||
msg_new_id = self.mail_message.create(cr, uid, {'body': 'My answer I am propagating', 'subject': 'Re: My subject', 'parent_id': msg1_id})
|
||||
|
||||
result = self.mail_message.message_quote_context(cr, uid, msg_new_id, limit=3)
|
||||
self.assertIn('Thread header about Zap Brannigan', result, 'Thread header content should be in quote.')
|
||||
self.assertIn('Second answer', result, 'Answer should be in quote.')
|
||||
self.assertIn('Third answer', result, 'Answer should be in quote.')
|
||||
self.assertIn('expandable', result, 'Expandable should be present.')
|
||||
self.assertNotIn('First answer, should not be displayed', result, 'Old answer should not be in quote.')
|
||||
self.assertNotIn('My answer I am propagating', result, 'Thread header content should be in quote.')
|
||||
|
||||
def test_21_message_post(self):
|
||||
""" Tests designed for message_post. """
|
||||
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'})
|
||||
|
@ -357,11 +403,11 @@ class test_mail(TestMailMockups):
|
|||
# the html2plaintext uses etree or beautiful soup, so the result may be slighly different
|
||||
# depending if you have installed beautiful soup.
|
||||
self.assertIn(sent_email['body_alternative'], _mail_bodyalt1 + '\nBert Tartopoils\n', 'sent_email body_alternative is incorrect')
|
||||
# Test: mail_message: partner_ids = group followers
|
||||
message_pids = set([partner.id for partner in message.partner_ids])
|
||||
# Test: mail_message: notified_partner_ids = group followers
|
||||
message_pids = set([partner.id for partner in message.notified_partner_ids])
|
||||
test_pids = set([p_b_id, p_c_id])
|
||||
self.assertEqual(test_pids, message_pids, 'mail.message partners incorrect')
|
||||
# Test: notification linked to this message = group followers = partner_ids
|
||||
# Test: notification linked to this message = group followers = notified_partner_ids
|
||||
notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
|
||||
notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)])
|
||||
self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect')
|
||||
|
@ -390,11 +436,11 @@ class test_mail(TestMailMockups):
|
|||
self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect')
|
||||
self.assertIn(_mail_body2, sent_email['body'], 'sent_email body incorrect')
|
||||
self.assertIn(_mail_bodyalt2, sent_email['body_alternative'], 'sent_email body_alternative incorrect')
|
||||
# Test: mail_message: partner_ids = group followers
|
||||
message_pids = set([partner.id for partner in message.partner_ids])
|
||||
# Test: mail_message: notified_partner_ids = group followers
|
||||
message_pids = set([partner.id for partner in message.notified_partner_ids])
|
||||
test_pids = set([p_b_id, p_c_id, p_d_id])
|
||||
self.assertEqual(message_pids, test_pids, 'mail.message partners incorrect')
|
||||
# Test: notifications linked to this message = group followers = partner_ids
|
||||
# Test: notifications linked to this message = group followers = notified_partner_ids
|
||||
notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
|
||||
notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)])
|
||||
self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect')
|
||||
|
@ -408,6 +454,11 @@ class test_mail(TestMailMockups):
|
|||
self.assertIn((attach.name, attach.datas.decode('base64')), _attachments,
|
||||
'mail.message attachment name / data incorrect')
|
||||
|
||||
# 3. Reply to the last message, check that its parent will be the first message
|
||||
msg_id3 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Test', parent_id=msg_id2)
|
||||
message = self.mail_message.browse(cr, uid, msg_id3)
|
||||
self.assertEqual(message.parent_id.id, msg_id, 'message_post did not flatten the thread structure')
|
||||
|
||||
def test_25_message_compose_wizard(self):
|
||||
""" Tests designed for the mail.compose.message wizard. """
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
|
@ -458,12 +509,12 @@ class test_mail(TestMailMockups):
|
|||
# Test: mail.message: subject, body inside pre
|
||||
self.assertEqual(message.subject, False, 'mail.message incorrect subject')
|
||||
self.assertEqual(message.body, _msg_body, 'mail.message incorrect body')
|
||||
# Test: mail.message: partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d)
|
||||
msg_pids = [partner.id for partner in message.partner_ids]
|
||||
# Test: mail.message: notified_partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d)
|
||||
msg_pids = [partner.id for partner in message.notified_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')
|
||||
self.assertEqual(set(msg_pids), set(test_pids), 'mail.message notified_partner_ids incorrect')
|
||||
|
||||
# ----------------------------------------
|
||||
# CASE2: reply to last comment with attachments
|
||||
|
@ -482,7 +533,7 @@ class test_mail(TestMailMockups):
|
|||
self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message incorrect content_subtype')
|
||||
# Test: mail.message: subject as Re:.., body in html, parent_id
|
||||
self.assertEqual(compose.subject, _msg_reply, 'mail.message incorrect subject')
|
||||
self.assertIn('Administrator wrote:<blockquote><pre>Pigs rules</pre></blockquote>', compose.body, 'mail.message body is incorrect')
|
||||
# self.assertIn('Administrator wrote:<blockquote><pre>Pigs rules</pre></blockquote>', compose.body, 'mail.message body is incorrect')
|
||||
self.assertEqual(compose.parent_id and compose.parent_id.id, message.id, 'mail.message parent_id incorrect')
|
||||
# Test: mail.message: attachments
|
||||
for attach in compose.attachment_ids:
|
||||
|
@ -522,15 +573,158 @@ class test_mail(TestMailMockups):
|
|||
|
||||
def test_30_message_read(self):
|
||||
""" Tests for message_read and expandables. """
|
||||
self.assertTrue(1 == 1, 'Test not implemented, do not replace by return True')
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
pigs_domain = [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)]
|
||||
|
||||
# Data: create a discussion in Pigs (2 messages, one with 2 and one with 3 answers)
|
||||
msg_id0 = self.group_pigs.message_post(body='0', subtype='mt_comment')
|
||||
msg_id1 = self.group_pigs.message_post(body='1', subtype='mt_comment')
|
||||
msg_id2 = self.group_pigs.message_post(body='2', subtype='mt_comment')
|
||||
msg_id3 = self.group_pigs.message_post(body='1-1', subtype='mt_comment', parent_id=msg_id1)
|
||||
msg_id4 = self.group_pigs.message_post(body='2-1', subtype='mt_comment', parent_id=msg_id2)
|
||||
msg_id5 = self.group_pigs.message_post(body='1-2', subtype='mt_comment', parent_id=msg_id1)
|
||||
msg_id6 = self.group_pigs.message_post(body='2-2', subtype='mt_comment', parent_id=msg_id2)
|
||||
msg_id7 = self.group_pigs.message_post(body='1-1-1', subtype='mt_comment', parent_id=msg_id3)
|
||||
msg_id8 = self.group_pigs.message_post(body='2-1-1', subtype='mt_comment', parent_id=msg_id4)
|
||||
msg_id9 = self.group_pigs.message_post(body='1-1-1', subtype='mt_comment', parent_id=msg_id3)
|
||||
msg_id10 = self.group_pigs.message_post(body='2-1-1', subtype='mt_comment', parent_id=msg_id4)
|
||||
msg_ids = [msg_id0, msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8, msg_id9, msg_id10]
|
||||
|
||||
# Test: read some specific ids
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, ids=msg_ids[2:4], domain=[('body', 'like', 'dummy')])
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list]
|
||||
self.assertEqual(msg_ids[2:4], read_msg_ids, 'message_read with direct ids should read only the requested ids')
|
||||
|
||||
# Test: read messages of Pigs through a domain, being thread or not threaded
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=200)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list]
|
||||
self.assertEqual(msg_ids, read_msg_ids, 'message_read flat with domain on Pigs should equal all messages of Pigs')
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=200, thread_level=1)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list]
|
||||
self.assertEqual(msg_ids, read_msg_ids, 'message_read threaded with domain on Pigs should equal all messages of Pigs')
|
||||
|
||||
# ----------------------------------------
|
||||
# CASE1: message_read with domain, threaded
|
||||
# We simulate an entire flow, using the expandables to test them
|
||||
# ----------------------------------------
|
||||
|
||||
# Do: read last message, threaded
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=1, thread_level=1)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
|
||||
# Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
|
||||
self.assertEqual(len(read_msg_list), 4, 'message_read on last Pigs message should return 2 messages and 2 expandables')
|
||||
self.assertEqual(set([msg_id2, msg_id10]), set(read_msg_ids), 'message_read on the last Pigs message should also get its parent')
|
||||
self.assertEqual(read_msg_list[1].get('parent_id'), read_msg_list[0].get('id'), 'message_read should set the ancestor to the thread header')
|
||||
# Data: get expandables
|
||||
new_threads_exp, new_msg_exp = None, None
|
||||
for msg in read_msg_list:
|
||||
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
|
||||
new_threads_exp = msg
|
||||
elif msg.get('type') == 'expandable':
|
||||
new_msg_exp = msg
|
||||
|
||||
# Do: fetch new messages in first thread, domain from expandable
|
||||
self.assertIsNotNone(new_msg_exp, 'message_read on last Pigs message should have returned a new messages expandable')
|
||||
domain = new_msg_exp.get('domain', [])
|
||||
# Test: expandable, conditions in domain
|
||||
self.assertIn(('id', 'child_of', msg_id2), domain, 'new messages expandable domain should contain a child_of condition')
|
||||
self.assertIn(('id', '>=', msg_id4), domain, 'new messages expandable domain should contain an id greater than condition')
|
||||
self.assertIn(('id', '<=', msg_id8), domain, 'new messages expandable domain should contain an id less than condition')
|
||||
self.assertEqual(new_msg_exp.get('parent_id'), msg_id2, 'new messages expandable should have ancestor_id set to the thread header')
|
||||
# Do: message_read with domain, thread_level=0, parent_id=msg_id2 (should be imposed by JS)
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=200, thread_level=0, parent_id=msg_id2)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
|
||||
# Test: other message in thread have been fetch
|
||||
self.assertEqual(set([msg_id4, msg_id6, msg_id8]), set(read_msg_ids), 'message_read in Pigs thread should return all the previous messages')
|
||||
|
||||
# Do: fetch a new thread, domain from expandable
|
||||
self.assertIsNotNone(new_threads_exp, 'message_read on last Pigs message should have returned a new threads expandable')
|
||||
domain = new_threads_exp.get('domain', [])
|
||||
# Test: expandable, conditions in domain
|
||||
for condition in pigs_domain:
|
||||
self.assertIn(condition, domain, 'new threads expandable domain should contain the message_read domain parameter')
|
||||
self.assertFalse(new_threads_exp.get('parent_id'), 'new threads expandable should not have an ancestor_id')
|
||||
# Do: message_read with domain, thread_level=1 (should be imposed by JS)
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=1, thread_level=1)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
|
||||
# Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
|
||||
self.assertEqual(len(read_msg_list), 4, 'message_read on Pigs should return 2 messages and 2 expandables')
|
||||
self.assertEqual(set([msg_id1, msg_id9]), set(read_msg_ids), 'message_read on a Pigs message should also get its parent')
|
||||
self.assertEqual(read_msg_list[1].get('parent_id'), read_msg_list[0].get('id'), 'message_read should set the ancestor to the thread header')
|
||||
# Data: get expandables
|
||||
new_threads_exp, new_msg_exp = None, None
|
||||
for msg in read_msg_list:
|
||||
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
|
||||
new_threads_exp = msg
|
||||
elif msg.get('type') == 'expandable':
|
||||
new_msg_exp = msg
|
||||
|
||||
# Do: fetch new messages in second thread, domain from expandable
|
||||
self.assertIsNotNone(new_msg_exp, 'message_read on Pigs message should have returned a new messages expandable')
|
||||
domain = new_msg_exp.get('domain', [])
|
||||
# Test: expandable, conditions in domain
|
||||
self.assertIn(('id', 'child_of', msg_id1), domain, 'new messages expandable domain should contain a child_of condition')
|
||||
self.assertIn(('id', '>=', msg_id3), domain, 'new messages expandable domain should contain an id greater than condition')
|
||||
self.assertIn(('id', '<=', msg_id7), domain, 'new messages expandable domain should contain an id less than condition')
|
||||
self.assertEqual(new_msg_exp.get('parent_id'), msg_id1, 'new messages expandable should have ancestor_id set to the thread header')
|
||||
# Do: message_read with domain, thread_level=0, parent_id=msg_id1 (should be imposed by JS)
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=200, thread_level=0, parent_id=msg_id1)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
|
||||
# Test: other message in thread have been fetch
|
||||
self.assertEqual(set([msg_id3, msg_id5, msg_id7]), set(read_msg_ids), 'message_read on the last Pigs message should also get its parent')
|
||||
|
||||
# Test: fetch a new thread, domain from expandable
|
||||
self.assertIsNotNone(new_threads_exp, 'message_read should have returned a new threads expandable')
|
||||
domain = new_threads_exp.get('domain', [])
|
||||
# Test: expandable, conditions in domain
|
||||
for condition in pigs_domain:
|
||||
self.assertIn(condition, domain, 'general expandable domain should contain the message_read domain parameter')
|
||||
# Do: message_read with domain, thread_level=1 (should be imposed by JS)
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=1, thread_level=1)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
|
||||
# Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
|
||||
self.assertEqual(len(read_msg_list), 1, 'message_read on Pigs should return 1 message because everything else has been fetched')
|
||||
self.assertEqual([msg_id0], read_msg_ids, 'message_read after 2 More should return only 1 last message')
|
||||
|
||||
# ----------------------------------------
|
||||
# CASE2: message_read with domain, flat
|
||||
# ----------------------------------------
|
||||
|
||||
# Do: read 2 lasts message, flat
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=2, thread_level=0)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
|
||||
# Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is not set, 1 expandable
|
||||
self.assertEqual(len(read_msg_list), 3, 'message_read on last Pigs message should return 2 messages and 1 expandable')
|
||||
self.assertEqual(set([msg_id9, msg_id10]), set(read_msg_ids), 'message_read flat on Pigs last messages should only return those messages')
|
||||
self.assertFalse(read_msg_list[0].get('parent_id'), 'message_read flat should set the ancestor as False')
|
||||
self.assertFalse(read_msg_list[1].get('parent_id'), 'message_read flat should set the ancestor as False')
|
||||
# Data: get expandables
|
||||
new_threads_exp, new_msg_exp = None, None
|
||||
for msg in read_msg_list:
|
||||
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
|
||||
new_threads_exp = msg
|
||||
|
||||
# Do: fetch new messages, domain from expandable
|
||||
self.assertIsNotNone(new_threads_exp, 'message_read flat on the 2 last Pigs messages should have returns a new threads expandable')
|
||||
domain = new_threads_exp.get('domain', [])
|
||||
# Test: expandable, conditions in domain
|
||||
for condition in pigs_domain:
|
||||
self.assertIn(condition, domain, 'new threads expandable domain should contain the message_read domain parameter')
|
||||
# Do: message_read with domain, thread_level=0 (should be imposed by JS)
|
||||
read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=20, thread_level=0)
|
||||
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
|
||||
# Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
|
||||
self.assertEqual(len(read_msg_list), 9, 'message_read on Pigs should return 9 messages and 0 expandable')
|
||||
self.assertEqual([msg_id0, msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8], read_msg_ids,
|
||||
'message_read, More on flat, should return all remaning messages')
|
||||
|
||||
def test_40_needaction(self):
|
||||
""" Tests for mail.message needaction. """
|
||||
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)
|
||||
user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
|
||||
group_pigs_demo = self.mail_group.browse(cr, self.user_raoul_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=[])
|
||||
na_demo_base = self.mail_message._needaction_count(cr, user_raoul.id, domain=[])
|
||||
|
||||
# Test: number of unread notification = needaction on mail.message
|
||||
notif_ids = self.mail_notification.search(cr, uid, [
|
||||
|
@ -558,12 +752,12 @@ class test_mail(TestMailMockups):
|
|||
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_demo.partner_id.id),
|
||||
('partner_id', '=', user_raoul.partner_id.id),
|
||||
('read', '=', False)
|
||||
])
|
||||
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)])
|
||||
na_demo = self.mail_message._needaction_count(cr, user_raoul.id, domain=[])
|
||||
na_demo_group = self.mail_message._needaction_count(cr, user_raoul.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')
|
||||
|
||||
|
@ -581,19 +775,11 @@ class test_mail(TestMailMockups):
|
|||
reply_msg = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1',
|
||||
extra='In-Reply-To: %s' % msg1.message_id)
|
||||
self.mail_group.message_process(cr, uid, None, reply_msg)
|
||||
# TDE note: temp various asserts because of the random bug about msg1.child_ids
|
||||
msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=1)
|
||||
new_msg = self.mail_message.browse(cr, uid, msg_ids[0])
|
||||
self.assertEqual(new_msg.parent_id, msg1, 'Newly processed mail_message (%d) should have msg1 as parent' % (new_msg.id))
|
||||
|
||||
# 2. References header
|
||||
reply_msg2 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: Re: 1',
|
||||
extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id)
|
||||
self.mail_group.message_process(cr, uid, None, reply_msg2)
|
||||
# TDE note: temp various asserts because of the random bug about msg1.child_ids
|
||||
msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=1)
|
||||
new_msg = self.mail_message.browse(cr, uid, msg_ids[0])
|
||||
self.assertEqual(new_msg.parent_id, msg1, 'Newly processed mail_message should have msg1 as parent')
|
||||
|
||||
# 3. Subject contains [<ID>] + model passed to message+process -> only attached to group, not to mail
|
||||
reply_msg3 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com',
|
||||
|
@ -613,34 +799,46 @@ class test_mail(TestMailMockups):
|
|||
|
||||
def test_60_message_vote(self):
|
||||
""" Test designed for the vote/unvote feature. """
|
||||
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)
|
||||
msg1 = group_pigs.message_post(body='My Body', subject='1')
|
||||
msg1 = self.mail_message.browse(cr, uid, msg1)
|
||||
cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs
|
||||
# Data: post a message on Pigs
|
||||
msg_id = group_pigs.message_post(body='My Body', subject='1')
|
||||
msg = self.mail_message.browse(cr, uid, msg_id)
|
||||
|
||||
# Create user Bert Tartopoils
|
||||
user_bert_id = self.res_users.create(cr, uid, {'name': 'Bert', 'login': 'bert'})
|
||||
user_bert = self.res_users.browse(cr, uid, user_bert_id)
|
||||
|
||||
# Test: msg1 and msg2 have void vote_user_ids
|
||||
self.assertFalse(msg1.vote_user_ids, 'newly created message msg1 has not void vote_user_ids')
|
||||
# Do: Admin vote for msg1
|
||||
self.mail_message.vote_toggle(cr, uid, [msg1.id])
|
||||
msg1.refresh()
|
||||
# Test: msg1 has Admin as voter
|
||||
self.assertEqual(set(msg1.vote_user_ids), set([user_admin]), 'after voting, Admin is not the voter')
|
||||
# Do: Bert vote for msg1
|
||||
self.mail_message.vote_toggle(cr, user_bert_id, [msg1.id])
|
||||
msg1.refresh()
|
||||
# Test: msg1 has Admin and Bert as voters
|
||||
self.assertEqual(set(msg1.vote_user_ids), set([user_admin, user_bert]), 'after voting, Admin and Bert are not the voters')
|
||||
# Do: Admin unvote for msg1
|
||||
self.mail_message.vote_toggle(cr, uid, [msg1.id])
|
||||
msg1.refresh()
|
||||
# Test: msg1 has Bert as voter
|
||||
self.assertEqual(set(msg1.vote_user_ids), set([user_bert]), 'after unvoting for Admin, Bert is not the voter')
|
||||
# Do: Admin vote for msg
|
||||
self.mail_message.vote_toggle(cr, uid, [msg.id])
|
||||
msg.refresh()
|
||||
# Test: msg has Admin as voter
|
||||
self.assertEqual(set(msg.vote_user_ids), set([user_admin]), 'mail_message vote: after voting, Admin should be in the voter')
|
||||
# Do: Bert vote for msg
|
||||
self.mail_message.vote_toggle(cr, user_raoul.id, [msg.id])
|
||||
msg.refresh()
|
||||
# Test: msg has Admin and Bert as voters
|
||||
self.assertEqual(set(msg.vote_user_ids), set([user_admin, user_raoul]), 'mail_message vote: after voting, Admin and Bert should be in the voters')
|
||||
# Do: Admin unvote for msg
|
||||
self.mail_message.vote_toggle(cr, uid, [msg.id])
|
||||
msg.refresh()
|
||||
# Test: msg has Bert as voter
|
||||
self.assertEqual(set(msg.vote_user_ids), set([user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter')
|
||||
|
||||
def test_70_message_favorite(self):
|
||||
""" Tests for favorites. """
|
||||
self.assertTrue(1 == 1, 'Test not implemented, do not replace by return True')
|
||||
cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs
|
||||
# Data: post a message on Pigs
|
||||
msg_id = group_pigs.message_post(body='My Body', subject='1')
|
||||
msg = self.mail_message.browse(cr, uid, msg_id)
|
||||
|
||||
# Do: Admin stars msg
|
||||
self.mail_message.favorite_toggle(cr, uid, [msg.id])
|
||||
msg.refresh()
|
||||
# Test: msg starred by Admin
|
||||
self.assertEqual(set(msg.favorite_user_ids), set([user_admin]), 'mail_message favorite: after starring, Admin should be in favorite_user_ids')
|
||||
# Do: Bert stars msg
|
||||
self.mail_message.favorite_toggle(cr, user_raoul.id, [msg.id])
|
||||
msg.refresh()
|
||||
# Test: msg starred by Admin and Raoul
|
||||
self.assertEqual(set(msg.favorite_user_ids), set([user_admin, user_raoul]), 'mail_message favorite: after starring, Admin and Raoul should be in favorite_user_ids')
|
||||
# Do: Admin unvote for msg
|
||||
self.mail_message.favorite_toggle(cr, uid, [msg.id])
|
||||
msg.refresh()
|
||||
# Test: msg starred by Raoul
|
||||
self.assertEqual(set(msg.favorite_user_ids), set([user_raoul]), 'mail_message favorite: after unstarring, Raoul should be in favorite_user_ids')
|
||||
|
|
|
@ -51,7 +51,40 @@ class test_mail_access_rights(test_mail.TestMailMockups):
|
|||
self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
|
||||
self.partner_raoul_id = self.user_raoul.partner_id.id
|
||||
|
||||
def test_00_mail_message_read_access_rights(self):
|
||||
def test_00_mail_message_search_access_rights(self):
|
||||
""" Test mail_message search override about access rights. """
|
||||
cr, uid, group_pigs_id = self.cr, self.uid, self.group_pigs_id
|
||||
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
|
||||
# Data: comment subtype for mail.message creation
|
||||
ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'mail', 'mt_comment')
|
||||
subtype_id = ref and ref[1] or False
|
||||
|
||||
# Data: Birds group, private
|
||||
group_birds_id = self.mail_group.create(self.cr, self.uid, {'name': 'Birds', 'public': 'private'})
|
||||
# Data: raoul is member of Pigs
|
||||
self.mail_group.message_subscribe(cr, uid, [group_pigs_id], [partner_raoul_id])
|
||||
# Data: various author_ids, partner_ids, documents
|
||||
msg_id1 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A', 'subtype_id': subtype_id})
|
||||
msg_id2 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A+B', 'partner_ids': [(6, 0, [partner_bert_id])], 'subtype_id': subtype_id})
|
||||
msg_id3 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A Pigs', 'model': 'mail.group', 'res_id': group_pigs_id, 'subtype_id': subtype_id})
|
||||
msg_id4 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A+B Pigs', 'model': 'mail.group', 'res_id': group_pigs_id, 'partner_ids': [(6, 0, [partner_bert_id])], 'subtype_id': subtype_id})
|
||||
msg_id5 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A+R Pigs', 'model': 'mail.group', 'res_id': group_pigs_id, 'partner_ids': [(6, 0, [partner_raoul_id])], 'subtype_id': subtype_id})
|
||||
msg_id6 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A Birds', 'model': 'mail.group', 'res_id': group_birds_id, 'subtype_id': subtype_id})
|
||||
msg_id7 = self.mail_message.create(cr, user_bert_id, {'subject': '_Test', 'body': 'B', 'subtype_id': subtype_id})
|
||||
msg_id8 = self.mail_message.create(cr, user_bert_id, {'subject': '_Test', 'body': 'B+R', 'partner_ids': [(6, 0, [partner_raoul_id])], 'subtype_id': subtype_id})
|
||||
|
||||
# Test: Bert: 2 messages that have Bert in partner_ids + 2 messages as author
|
||||
msg_ids = self.mail_message.search(cr, user_bert_id, [('subject', 'like', '_Test')])
|
||||
self.assertEqual(set([msg_id2, msg_id4, msg_id7, msg_id8]), set(msg_ids), 'mail_message search failed')
|
||||
# Test: Raoul: 3 messages on Pigs Raoul can read (employee can read group with default values), 0 on Birds (private group)
|
||||
msg_ids = self.mail_message.search(cr, user_raoul_id, [('subject', 'like', '_Test'), ('body', 'like', 'A')])
|
||||
self.assertEqual(set([msg_id3, msg_id4, msg_id5]), set(msg_ids), 'mail_message search failed')
|
||||
# Test: Admin: all messages
|
||||
msg_ids = self.mail_message.search(cr, uid, [('subject', 'like', '_Test')])
|
||||
self.assertEqual(set([msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8]), set(msg_ids), 'mail_message search failed')
|
||||
|
||||
def test_05_mail_message_read_access_rights(self):
|
||||
""" Test basic mail_message read access rights. """
|
||||
cr, uid = self.cr, self.uid
|
||||
partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
|
||||
|
@ -98,10 +131,6 @@ class test_mail_access_rights(test_mail.TestMailMockups):
|
|||
self.assertRaises(except_orm, self.mail_message.read,
|
||||
cr, user_bert_id, message_id)
|
||||
|
||||
def test_05_mail_message_search_access_rights(self):
|
||||
""" 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
|
||||
|
|
|
@ -156,12 +156,6 @@ class mail_compose_message(osv.TransientModel):
|
|||
reply_subject = tools.ustr(message_data.subject or '')
|
||||
if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)) and message_data.subject:
|
||||
reply_subject = "%s %s" % (re_prefix, reply_subject)
|
||||
# create the reply in the body
|
||||
reply_body = _('<div>On %(date)s, %(sender_name)s wrote:<blockquote>%(body)s</blockquote></div>') % {
|
||||
'date': message_data.date if message_data.date else '',
|
||||
'sender_name': message_data.author_id.name,
|
||||
'body': message_data.body,
|
||||
}
|
||||
# get partner_ids from original message
|
||||
partner_ids = [partner.id for partner in message_data.partner_ids] if message_data.partner_ids else []
|
||||
|
||||
|
@ -170,7 +164,6 @@ class mail_compose_message(osv.TransientModel):
|
|||
'model': message_data.model,
|
||||
'res_id': message_data.res_id,
|
||||
'parent_id': message_data.id,
|
||||
'body': reply_body,
|
||||
'subject': reply_subject,
|
||||
'partner_ids': partner_ids,
|
||||
'content_subtype': 'html',
|
||||
|
|
|
@ -14,22 +14,16 @@
|
|||
<field name="parent_id" invisible="1"/>
|
||||
<field name="content_subtype" invisible="1"/>
|
||||
<!-- visible wizard -->
|
||||
<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)"/>
|
||||
on_change="onchange_partner_ids(partner_ids)" required="1"/>
|
||||
<field name="subject" placeholder="Subject..."
|
||||
attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Body">
|
||||
<field name="body" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Attachments">
|
||||
<field name="attachment_ids" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<field name="body"/>
|
||||
<field name="attachment_ids" widget="one2many_binary" blockui="0"/>
|
||||
<footer>
|
||||
<button string="Send" name="send_mail" type="object" class="oe_highlight" />
|
||||
<button string="Send" name="send_mail" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
|
|
|
@ -7,14 +7,14 @@ msgstr ""
|
|||
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:49+0000\n"
|
||||
"PO-Revision-Date: 2012-02-13 09:52+0000\n"
|
||||
"Last-Translator: Jeff Wang <wjfonhand@hotmail.com>\n"
|
||||
"PO-Revision-Date: 2012-10-25 17:08+0000\n"
|
||||
"Last-Translator: ccdos <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-19 05:06+0000\n"
|
||||
"X-Generator: Launchpad (build 16165)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-26 04:56+0000\n"
|
||||
"X-Generator: Launchpad (build 16194)\n"
|
||||
|
||||
#. module: mrp
|
||||
#: view:mrp.routing.workcenter:0
|
||||
|
@ -89,7 +89,7 @@ msgstr "单位成本"
|
|||
#. module: mrp
|
||||
#: view:mrp.production:0
|
||||
msgid "Scrap Products"
|
||||
msgstr "原料消耗"
|
||||
msgstr "报废原料"
|
||||
|
||||
#. module: mrp
|
||||
#: model:ir.actions.act_window,name:mrp.mrp_routing_action
|
||||
|
@ -135,7 +135,7 @@ msgstr "生产订单编号"
|
|||
#. module: mrp
|
||||
#: view:mrp.production:0
|
||||
msgid "Finished Products"
|
||||
msgstr "已完工数量"
|
||||
msgstr "产成品"
|
||||
|
||||
#. module: mrp
|
||||
#: view:mrp.production:0
|
||||
|
@ -209,7 +209,7 @@ msgstr "输入一个费用产品用于跟踪辅助核算会计中的成本"
|
|||
#. module: mrp
|
||||
#: model:process.node,note:mrp.process_node_purchaseprocure0
|
||||
msgid "For purchased material"
|
||||
msgstr "用于采购原料"
|
||||
msgstr "用于外购的物料"
|
||||
|
||||
#. module: mrp
|
||||
#: model:ir.actions.act_window,help:mrp.product_form_config_action
|
||||
|
@ -1288,7 +1288,7 @@ msgstr "如果产品的供应方法是外购,系统创建采购订单。"
|
|||
#. module: mrp
|
||||
#: model:ir.model,name:mrp.model_procurement_order
|
||||
msgid "Procurement"
|
||||
msgstr "需求单"
|
||||
msgstr "需求"
|
||||
|
||||
#. module: mrp
|
||||
#: model:ir.actions.act_window,name:mrp.action_view_mrp_product_price_wizard
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue