[[ get_account(data) or removeParentNode('para') ]]
[[ get_fiscalyear(data) or '' ]]
- [[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]]
+ [[ get_filter(data)=='No Filters' and get_filter(data) or removeParentNode('para') ]]
[[ get_filter(data)=='Date' or removeParentNode('blockTable') ]]
Start Date
diff --git a/addons/account/report/account_general_ledger.py b/addons/account/report/account_general_ledger.py
index 294c7fa99cf..38f2392f740 100644
--- a/addons/account/report/account_general_ledger.py
+++ b/addons/account/report/account_general_ledger.py
@@ -299,10 +299,10 @@ class general_ledger(report_sxw.rml_parse, common_report_header):
def _get_sortby(self, data):
if self.sortby == 'sort_date':
- return 'Date'
+ return self._translate('Date')
elif self.sortby == 'sort_journal_partner':
- return 'Journal & Partner'
- return 'Date'
+ return self._translate('Journal & Partner')
+ return self._translate('Date')
report_sxw.report_sxw('report.account.general.ledger', 'account.account', 'addons/account/report/account_general_ledger.rml', parser=general_ledger, header='internal')
report_sxw.report_sxw('report.account.general.ledger_landscape', 'account.account', 'addons/account/report/account_general_ledger_landscape.rml', parser=general_ledger, header='internal landscape')
diff --git a/addons/account/report/common_report_header.py b/addons/account/report/common_report_header.py
index c93c3e02ee3..c5719d98b96 100644
--- a/addons/account/report/common_report_header.py
+++ b/addons/account/report/common_report_header.py
@@ -94,10 +94,10 @@ class common_report_header(object):
def _get_filter(self, data):
if data.get('form', False) and data['form'].get('filter', False):
if data['form']['filter'] == 'filter_date':
- return 'Date'
+ return self._translate('Date')
elif data['form']['filter'] == 'filter_period':
- return 'Periods'
- return 'No Filter'
+ return self._translate('Periods')
+ return self._translate('No Filters')
def _sum_debit_period(self, period_id, journal_id=None):
journals = journal_id or self.journal_ids
diff --git a/addons/account/static/src/js/account_move_reconciliation.js b/addons/account/static/src/js/account_move_reconciliation.js
index 8e72e24eb22..22fe9b7b392 100644
--- a/addons/account/static/src/js/account_move_reconciliation.js
+++ b/addons/account/static/src/js/account_move_reconciliation.js
@@ -48,7 +48,7 @@ openerp.account = function (instance) {
this.last_group_by = group_by;
this.old_search = _.bind(this._super, this);
var mod = new instance.web.Model("account.move.line", context, domain);
- return mod.call("list_partners_to_reconcile", []).pipe(function(result) {
+ return mod.call("list_partners_to_reconcile", []).then(function(result) {
var current = self.current_partner !== null ? self.partners[self.current_partner][0] : null;
self.partners = result;
var index = _.find(_.range(self.partners.length), function(el) {
@@ -74,7 +74,7 @@ openerp.account = function (instance) {
return fct();
} else {
return new instance.web.Model("res.partner").call("read",
- [self.partners[self.current_partner][0], ["last_reconciliation_date"]]).pipe(function(res) {
+ [self.partners[self.current_partner][0], ["last_reconciliation_date"]]).then(function(res) {
self.last_reconciliation_date =
instance.web.format_value(res.last_reconciliation_date, {"type": "datetime"}, _t("Never"));
return fct();
@@ -92,7 +92,7 @@ openerp.account = function (instance) {
return false;
}
- new instance.web.Model("ir.model.data").call("get_object_reference", ["account", "action_view_account_move_line_reconcile"]).pipe(function(result) {
+ new instance.web.Model("ir.model.data").call("get_object_reference", ["account", "action_view_account_move_line_reconcile"]).then(function(result) {
var additional_context = _.extend({
active_id: ids[0],
active_ids: ids,
@@ -101,7 +101,7 @@ openerp.account = function (instance) {
return self.rpc("/web/action/load", {
action_id: result[1],
context: additional_context
- }).then(function (result) {
+ }).done(function (result) {
result.context = _.extend(result.context || {}, additional_context);
result.flags = result.flags || {};
result.flags.new_window = true;
@@ -116,7 +116,7 @@ openerp.account = function (instance) {
mark_as_reconciled: function() {
var self = this;
var id = self.partners[self.current_partner][0];
- new instance.web.Model("res.partner").call("mark_as_reconciled", [[id]]).pipe(function() {
+ new instance.web.Model("res.partner").call("mark_as_reconciled", [[id]]).then(function() {
self.do_search(self.last_domain, self.last_context, self.last_group_by);
});
},
diff --git a/addons/account/test/account_report.yml b/addons/account/test/account_report.yml
index 64f8ccf838f..7fe3095d47f 100644
--- a/addons/account/test/account_report.yml
+++ b/addons/account/test/account_report.yml
@@ -31,7 +31,6 @@
-
!python {model: account.account}: |
ctx={}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')],'active_id':ref('account.chart0')})
data_dict = {'chart_account_id':ref('account.chart0')}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_aged_balance_view',wiz_data=data_dict, context=ctx, our_module='account')
@@ -40,7 +39,6 @@
-
!python {model: account.account}: |
ctx={}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
data_dict = {'chart_account_id':ref('account.chart0'), 'account_report_id': ref('account_financial_report_balancesheet0')}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_report',wiz_data=data_dict, context=ctx, our_module='account')
@@ -49,7 +47,6 @@
-
!python {model: account.account}: |
ctx={}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
data_dict = {'chart_account_id':ref('account.chart0')}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_balance_menu',wiz_data=data_dict, context=ctx, our_module='account')
@@ -60,7 +57,6 @@
journal_ids = [ref('account.sales_journal'),ref('account.refund_sales_journal'),ref('account.expenses_journal'),ref('account.refund_expenses_journal'),
ref('account.bank_journal'),ref('account.check_journal'),ref('account.cash_journal')]
ctx={}
- ctx.update({'model': 'account.journal.period','active_ids':journal_ids})
data_dict = {'chart_account_id':ref('account.chart0')}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_central_journal',wiz_data=data_dict, context=ctx, our_module='account')
@@ -71,7 +67,6 @@
journal_ids = [ref('account.sales_journal'),ref('account.refund_sales_journal'),ref('account.expenses_journal'),ref('account.refund_expenses_journal'),
ref('account.bank_journal'),ref('account.check_journal'),ref('account.cash_journal')]
ctx={}
- ctx.update({'model': 'account.journal.period','active_ids':journal_ids})
data_dict = {'chart_account_id':ref('account.chart0')}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_general_journal',wiz_data=data_dict, context=ctx, our_module='account')
@@ -80,7 +75,6 @@
-
!python {model: account.account}: |
ctx={}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
data_dict = {'chart_account_id':ref('account.chart0'),'landscape':False}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_general_ledger_menu',wiz_data=data_dict, context=ctx, our_module='account')
@@ -89,7 +83,6 @@
-
!python {model: account.account}: |
ctx={}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
data_dict = {'chart_account_id':ref('account.chart0'),'landscape':True}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_general_ledger_menu',wiz_data=data_dict, context=ctx, our_module='account')
@@ -99,7 +92,6 @@
!python {model: account.journal.period}: |
journal_ids = [ref('account.sales_journal'),ref('account.refund_sales_journal'),ref('account.expenses_journal'),ref('account.refund_expenses_journal'),ref('account.bank_journal'),ref('account.check_journal'),ref('account.cash_journal')]
ctx={}
- ctx.update({'model': 'account.journal.period','active_ids':journal_ids})
data_dict = {'chart_account_id':ref('account.chart0'), 'period_from':ref('period_1'), 'period_to':ref('period_12')}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_print_journal',wiz_data=data_dict, context=ctx, our_module='account')
@@ -109,7 +101,6 @@
!python {model: account.account}: |
ctx={}
data_dict = {'chart_account_id':ref('account.chart0')}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')],'active_id':ref('account.chart0')})
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_partner_balance',wiz_data=data_dict, context=ctx, our_module='account')
-
@@ -118,7 +109,6 @@
!python {model: account.account}: |
ctx={}
data_dict = {'chart_account_id':ref('account.chart0'),'page_split': True}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')],'active_id':ref('account.chart0')})
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_partner_ledger',wiz_data=data_dict, context=ctx, our_module='account')
-
@@ -127,7 +117,6 @@
!python {model: res.partner}: |
ctx={}
data_dict = {'chart_account_id':ref('account.chart0'),'page_split': False}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')],'active_id':ref('account.chart0')})
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_partner_ledger',wiz_data=data_dict, context=ctx, our_module='account')
-
@@ -135,7 +124,6 @@
-
!python {model: account.account}: |
ctx={}
- ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
data_dict = {'chart_account_id':ref('account.chart0'), 'target_move': 'all', 'account_report_id': ref('account_financial_report_balancesheet0')}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_report',wiz_data=data_dict, context=ctx, our_module='account')
diff --git a/addons/account/wizard/account_fiscalyear_close.py b/addons/account/wizard/account_fiscalyear_close.py
index 196c14d3792..dbf815d5218 100644
--- a/addons/account/wizard/account_fiscalyear_close.py
+++ b/addons/account/wizard/account_fiscalyear_close.py
@@ -226,7 +226,7 @@ class account_fiscalyear_close(osv.osv_memory):
for account in obj_acc_account.browse(cr, uid, account_ids, context={'fiscalyear': fy_id}):
balance_in_currency = 0.0
if account.currency_id:
- cr.execute('SELECT sum(amount_currency) as balance_in_currency FROM account_move_line ' \
+ cr.execute('SELECT sum(COALESCE(amount_currency,0.0)) as balance_in_currency FROM account_move_line ' \
'WHERE account_id = %s ' \
'AND ' + query_line + ' ' \
'AND currency_id = %s', (account.id, account.currency_id.id))
diff --git a/addons/account/wizard/account_report_common.py b/addons/account/wizard/account_report_common.py
index 9acc09e9026..4438484aaaf 100644
--- a/addons/account/wizard/account_report_common.py
+++ b/addons/account/wizard/account_report_common.py
@@ -119,7 +119,11 @@ class account_common_report(osv.osv_memory):
def _get_fiscalyear(self, cr, uid, context=None):
now = time.strftime('%Y-%m-%d')
- fiscalyears = self.pool.get('account.fiscalyear').search(cr, uid, [('date_start', '<', now), ('date_stop', '>', now)], limit=1 )
+ company_id = False
+ ids = context.get('active_ids', [])
+ if ids:
+ company_id = self.browse(cr, uid, ids[0], context=context).company_id.id
+ fiscalyears = self.pool.get('account.fiscalyear').search(cr, uid, [('date_start', '<', now), ('date_stop', '>', now), ('company_id', '=', company_id)], limit=1)
return fiscalyears and fiscalyears[0] or False
def _get_all_journal(self, cr, uid, context=None):
diff --git a/addons/account_analytic_plans/account_analytic_plans_view.xml b/addons/account_analytic_plans/account_analytic_plans_view.xml
index 76d6aeed7b8..e5c02c8640e 100644
--- a/addons/account_analytic_plans/account_analytic_plans_view.xml
+++ b/addons/account_analytic_plans/account_analytic_plans_view.xml
@@ -21,17 +21,17 @@
-
+
-
+
-
+
@@ -276,7 +276,7 @@
-
+
account.analytic.default.tree.plans
account.analytic.default
diff --git a/addons/account_anglo_saxon/invoice.py b/addons/account_anglo_saxon/invoice.py
index 693283b870b..26271015b7e 100644
--- a/addons/account_anglo_saxon/invoice.py
+++ b/addons/account_anglo_saxon/invoice.py
@@ -40,7 +40,7 @@ class account_invoice_line(osv.osv):
if inv.type in ('out_invoice','out_refund'):
for i_line in inv.invoice_line:
- if i_line.product_id:
+ if i_line.product_id and i_line.product_id.valuation == 'real_time':
if inv.type == 'out_invoice':
# debit account dacc will be the output account
# first check the product, if empty check the category
@@ -87,7 +87,7 @@ class account_invoice_line(osv.osv):
})
elif inv.type in ('in_invoice','in_refund'):
for i_line in inv.invoice_line:
- if i_line.product_id:
+ if i_line.product_id and i_line.product_id.valuation == 'real_time':
if i_line.product_id.type != 'service':
# get the price difference account at the product
acc = i_line.product_id.property_account_creditor_price_difference and i_line.product_id.property_account_creditor_price_difference.id
diff --git a/addons/account_asset/account_asset.py b/addons/account_asset/account_asset.py
index ac9024fd51d..0dddf441c59 100644
--- a/addons/account_asset/account_asset.py
+++ b/addons/account_asset/account_asset.py
@@ -386,7 +386,7 @@ class account_asset_depreciation_line(osv.osv):
current_currency = line.asset_id.currency_id.id
context.update({'date': depreciation_date})
amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
- sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
+ sign = (line.asset_id.category_id.journal_id.type == 'purchase' and 1) or -1
asset_name = line.asset_id.name
reference = line.name
move_vals = {
diff --git a/addons/account_budget/account_budget_view.xml b/addons/account_budget/account_budget_view.xml
index cb0466a1c92..d24124e307d 100644
--- a/addons/account_budget/account_budget_view.xml
+++ b/addons/account_budget/account_budget_view.xml
@@ -220,7 +220,18 @@
-
+
+
+ account.budget.line.search
+ crossovered.budget.lines
+ search
+
+
+
+
+
+
+
crossovered.budget.line.tree
crossovered.budget.lines
diff --git a/addons/account_check_writing/i18n/es.po b/addons/account_check_writing/i18n/es.po
index 81932642524..c473e077210 100644
--- a/addons/account_check_writing/i18n/es.po
+++ b/addons/account_check_writing/i18n/es.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
-"PO-Revision-Date: 2012-02-09 19:28+0000\n"
-"Last-Translator: FULL NAME \n"
+"PO-Revision-Date: 2012-11-09 12:09+0000\n"
+"Last-Translator: Pedro Manuel Baeza \n"
"Language-Team: Spanish \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-30 05:36+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-10 04:59+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
#. module: account_check_writing
#: selection:res.company,check_layout:0
@@ -154,7 +154,7 @@ msgstr "Compañías"
#. module: account_check_writing
#: view:res.company:0
msgid "Default Check Layout"
-msgstr ""
+msgstr "Comprobar formato por defecto"
#. module: account_check_writing
#: constraint:account.journal:0
diff --git a/addons/account_sequence/account_sequence_data.xml b/addons/account_sequence/account_sequence_data.xml
index 1ad362b3979..b2d40412d0d 100644
--- a/addons/account_sequence/account_sequence_data.xml
+++ b/addons/account_sequence/account_sequence_data.xml
@@ -43,6 +43,18 @@
+
+ Account Journal
+ account.journal
+
+
+
+ Account journal sequence
+ account.journal
+ AJ
+
+
+
diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py
index 8bd9a48b1f4..b0c53b72835 100644
--- a/addons/account_voucher/account_voucher.py
+++ b/addons/account_voucher/account_voucher.py
@@ -267,6 +267,7 @@ class account_voucher(osv.osv):
_order = "date desc, id desc"
# _rec_name = 'number'
_columns = {
+ 'active': fields.boolean('Active', help="By default, reconciliation vouchers made on draft bank statements are set as inactive, which allow to hide the customer/supplier payment while the bank statement isn't confirmed."),
'type':fields.selection([
('sale','Sale'),
('purchase','Purchase'),
@@ -328,6 +329,7 @@ class account_voucher(osv.osv):
'is_multi_currency': fields.boolean('Multi Currency Voucher', help='Fields with internal purpose only that depicts if the voucher is a multi currency one or not'),
}
_defaults = {
+ 'active': True,
'period_id': _get_period,
'partner_id': _get_partner,
'journal_id':_get_journal,
@@ -953,6 +955,9 @@ class account_voucher(osv.osv):
if voucher_brw.number:
name = voucher_brw.number
elif voucher_brw.journal_id.sequence_id:
+ if not voucher_brw.journal_id.sequence_id.active:
+ raise osv.except_osv(_('Configuration Error !'),
+ _('Please activate the sequence of selected journal !'))
name = seq_obj.next_by_id(cr, uid, voucher_brw.journal_id.sequence_id.id, context=context)
else:
raise osv.except_osv(_('Error!'),
@@ -1504,6 +1509,15 @@ account_voucher_line()
class account_bank_statement(osv.osv):
_inherit = 'account.bank.statement'
+ def button_confirm_bank(self, cr, uid, ids, context=None):
+ voucher_obj = self.pool.get('account.voucher')
+ voucher_ids = []
+ for statement in self.browse(cr, uid, ids, context=context):
+ voucher_ids += [line.voucher_id.id for line in statement.line_ids if line.voucher_id]
+ if voucher_ids:
+ voucher_obj.write(cr, uid, voucher_ids, {'active': True}, context=context)
+ return super(account_bank_statement, self).button_confirm_bank(cr, uid, ids, context=context)
+
def button_cancel(self, cr, uid, ids, context=None):
voucher_obj = self.pool.get('account.voucher')
for st in self.browse(cr, uid, ids, context=context):
@@ -1539,6 +1553,16 @@ account_bank_statement()
class account_bank_statement_line(osv.osv):
_inherit = 'account.bank.statement.line'
+ def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
+ res = super(account_bank_statement_line, self).onchange_partner_id(cr, uid, ids, partner_id, context=context)
+ if 'value' not in res:
+ res['value'] = {}
+ res['value'].update({'voucher_id' : False})
+ return res
+
+ def onchange_amount(self, cr, uid, ids, amount, context=None):
+ return {'value' : {'voucher_id' : False}}
+
def _amount_reconciled(self, cursor, user, ids, name, args, context=None):
if not ids:
return {}
@@ -1565,7 +1589,7 @@ class account_bank_statement_line(osv.osv):
_columns = {
'amount_reconciled': fields.function(_amount_reconciled,
string='Amount reconciled', type='float'),
- 'voucher_id': fields.many2one('account.voucher', 'Payment'),
+ 'voucher_id': fields.many2one('account.voucher', 'Reconciliation'),
}
def unlink(self, cr, uid, ids, context=None):
diff --git a/addons/account_voucher/account_voucher_view.xml b/addons/account_voucher/account_voucher_view.xml
index babe60b640e..2cb393f4008 100644
--- a/addons/account_voucher/account_voucher_view.xml
+++ b/addons/account_voucher/account_voucher_view.xml
@@ -200,33 +200,29 @@
-
+
account.bank.statement.voucher.tree.inherit
account.bank.statement
-
+
+
+
+
+
+ onchange_amount(amount)
+
-
- account.bank.statement.voucher.form.inherit
- account.bank.statement
-
-
-
-
-
-
-
account.cash.statement.voucher.tree.inherit
account.bank.statement
diff --git a/addons/account_voucher/i18n/fr.po b/addons/account_voucher/i18n/fr.po
index ea825985448..bec649eaea1 100644
--- a/addons/account_voucher/i18n/fr.po
+++ b/addons/account_voucher/i18n/fr.po
@@ -7,14 +7,15 @@ 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-05-10 17:31+0000\n"
-"Last-Translator: Fabien (Open ERP) \n"
+"PO-Revision-Date: 2012-11-07 13:27+0000\n"
+"Last-Translator: Frederic Clementi - Camptocamp.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-30 05:19+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-08 04:47+0000\n"
+"X-Generator: Launchpad (build 16232)\n"
#. module: account_voucher
#: view:sale.receipt.report:0
@@ -469,7 +470,7 @@ msgstr "Délai moyen de règlement"
#. module: account_voucher
#: field:res.company,income_currency_exchange_account_id:0
msgid "Income Currency Rate"
-msgstr "Taux de change d'achat"
+msgstr "Compte de gain de change"
#. module: account_voucher
#: code:addons/account_voucher/account_voucher.py:1063
@@ -625,9 +626,9 @@ msgid ""
"Unable to create accounting entry for currency rate difference. You have to "
"configure the field 'Income Currency Rate' on the company! "
msgstr ""
-"Impossible de créer une entrée de la comptabilité à cause de la différence "
-"de taux de change. Vous devez configurer le champ 'Taux de change de vente' "
-"sur la société! "
+"Impossible de créer une écriture comptable à cause de la différence de taux "
+"de change. Vous devez configurer le champ 'Compte de gain de change' au "
+"niveau du formulaire de la société! "
#. module: account_voucher
#: view:account.voucher:0 view:sale.receipt.report:0
@@ -802,7 +803,7 @@ msgstr "Factures et transactions exceptionnelles"
#. module: account_voucher
#: field:res.company,expense_currency_exchange_account_id:0
msgid "Expense Currency Rate"
-msgstr "Taux de change de la dépense"
+msgstr "Compte de perte de change"
#. module: account_voucher
#: sql_constraint:account.invoice:0
@@ -1089,9 +1090,9 @@ msgid ""
"Unable to create accounting entry for currency rate difference. You have to "
"configure the field 'Expense Currency Rate' on the company! "
msgstr ""
-"Impossible de créer une entrée en comptabilité pour la différence de taux de "
-"change. Vous devez configurer le champ \"Taux de change d'achat\" de la "
-"société ! "
+"Impossible de créer une écriture comptable à cause de la différence de taux "
+"de change. Vous devez configurer le champ 'Compte de perte de change' au "
+"niveau du formulaire de la société! "
#. module: account_voucher
#: field:account.voucher,type:0
@@ -1156,7 +1157,7 @@ msgstr "Année"
#. module: account_voucher
#: field:account.voucher.line,amount_unreconciled:0
msgid "Open Balance"
-msgstr "Solde initial"
+msgstr "Restant dû"
#. module: account_voucher
#: view:account.voucher:0 field:account.voucher,amount:0
diff --git a/addons/account_voucher/voucher_payment_receipt_view.xml b/addons/account_voucher/voucher_payment_receipt_view.xml
index 881a5e38f1c..b6f367601ff 100644
--- a/addons/account_voucher/voucher_payment_receipt_view.xml
+++ b/addons/account_voucher/voucher_payment_receipt_view.xml
@@ -54,6 +54,8 @@
-
+
crm.case.categ.form
@@ -238,7 +237,7 @@
-
+
@@ -297,7 +296,7 @@
+ parent="base.menu_crm_config_lead"/>
Cases by Sales Team
@@ -354,13 +353,13 @@
+ icon="gtk-execute"/>
+ icon="gtk-cancel"/>
+ icon="gtk-go-forward"/>
@@ -412,7 +411,7 @@
tree,form
- Click to define a new customer segmentation.
+ Click to define a new customer segmentation.
Create specific categories which you can assign to your
contacts to better manage your interactions with them. The
@@ -425,7 +424,7 @@
+ parent="base.menu_base_config"/>
@@ -466,11 +465,11 @@
form
tree,form
-
+
+ parent="base.menu_crm_config_lead"/>
diff --git a/addons/crm/i18n/es.po b/addons/crm/i18n/es.po
index fe2f139f682..a72a6ec2305 100644
--- a/addons/crm/i18n/es.po
+++ b/addons/crm/i18n/es.po
@@ -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-02-10 17:48+0000\n"
-"Last-Translator: Carlos Ch. \n"
+"PO-Revision-Date: 2012-11-09 12:10+0000\n"
+"Last-Translator: Pedro Manuel Baeza \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-30 04:57+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-10 04:59+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
#. module: crm
#: view:crm.lead.report:0
@@ -172,7 +172,7 @@ msgstr "Mes esperado de cierre"
#. module: crm
#: view:crm.lead2opportunity.partner.mass:0
msgid "Assigned Opportunities to"
-msgstr ""
+msgstr "Oportunidades asignadas a"
#. module: crm
#: view:crm.lead:0 field:crm.lead,partner_id:0 view:crm.lead.report:0
@@ -598,7 +598,7 @@ msgstr "Fecha de fin"
#. module: crm
#: view:crm.opportunity2phonecall:0 view:crm.phonecall2phonecall:0
msgid "Schedule/Log a Call"
-msgstr ""
+msgstr "Planificar/Registrar una llamada"
#. module: crm
#: constraint:base.action.rule:0
@@ -792,7 +792,7 @@ msgstr "Siguiente"
#. module: crm
#: field:crm.segmentation,som_interval:0
msgid "Days per Period"
-msgstr ""
+msgstr "Días por periodo"
#. module: crm
#: field:crm.meeting,byday:0
@@ -959,7 +959,7 @@ msgstr "Días para abrir"
#. module: crm
#: view:crm.meeting:0
msgid "Show Time as"
-msgstr ""
+msgstr "Mostrar hora como"
#. module: crm
#: view:crm.phonecall2partner:0
@@ -1342,7 +1342,7 @@ msgstr "Fecha escritura"
#. module: crm
#: view:crm.meeting:0
msgid "End of Recurrency"
-msgstr ""
+msgstr "Fin de recurrencia"
#. module: crm
#: view:crm.meeting:0
@@ -1392,6 +1392,12 @@ msgid ""
" \n"
"If the call needs to be done then the state is set to 'Not Held'."
msgstr ""
+"El estado se establece a 'Para hacer' cuando se crea el caso.\n"
+"Si es caso está en marcha, el estado se establece a 'Abierto.\n"
+"Cuando la llamada se termina, el estado se establece en 'Realizada'. "
+" \n"
+"Si la llamada está pendiente de ser realizada, entonces el estado se "
+"establece en 'Pendiente'."
#. module: crm
#: selection:crm.meeting,week_list:0
@@ -1449,7 +1455,7 @@ msgstr "Oportunidades por categorías"
#. module: crm
#: model:crm.case.section,name:crm.section_sales_marketing_department
msgid "Sales Marketing Department"
-msgstr ""
+msgstr "Departamento de ventas y marketing"
#. module: crm
#: view:crm.phonecall.report:0
@@ -1900,7 +1906,7 @@ msgstr "Responder a"
#. module: crm
#: view:crm.case.section:0
msgid "Select Stages for this Sales Team"
-msgstr ""
+msgstr "Seleccione etapas para este equipo de ventas"
#. module: crm
#: view:board.board:0
@@ -2015,7 +2021,7 @@ msgstr "Ubicación"
#. module: crm
#: model:ir.model,name:crm.model_crm_lead2opportunity_partner_mass
msgid "Mass Lead To Opportunity Partner"
-msgstr ""
+msgstr "Transformación masiva de iniciativa a oportunidad"
#. module: crm
#: view:crm.lead:0
@@ -2777,7 +2783,7 @@ msgstr "e-mail del contacto"
#. module: crm
#: field:crm.lead,referred:0
msgid "Referred by"
-msgstr ""
+msgstr "Referido por"
#. module: crm
#: view:crm.lead:0 model:ir.model,name:crm.model_crm_add_note
@@ -3541,7 +3547,7 @@ msgstr "Nuevas oportunidades"
#: code:addons/crm/crm_action_rule.py:61
#, python-format
msgid "No E-Mail Found for your Company address!"
-msgstr ""
+msgstr "No se encontró dirección de e-mail para el contacto de su compañía"
#. module: crm
#: field:crm.lead.report,email:0
@@ -3561,7 +3567,7 @@ msgstr "Oportunidades por usuario y equipo"
#. module: crm
#: view:crm.phonecall:0
msgid "Reset to Todo"
-msgstr ""
+msgstr "Cambiar a 'Para hacer'"
#. module: crm
#: field:crm.case.section,working_hours:0
@@ -3643,6 +3649,10 @@ msgid ""
"partner. From the phone call form, you can trigger a request for another "
"call, a meeting or an opportunity."
msgstr ""
+"Esta herramienta permite registrar sus llamadas entrantes sobre la marcha. "
+"Cada llamada que recibe aparecerá en el formulario de la empresa para trazar "
+"cada contacto que tiene con una empresa. Desde el formulario de llamadas, "
+"puede lanzar una petición para otra llamada, una reunión u oportunidad."
#. module: crm
#: selection:crm.lead.report,creation_month:0
@@ -3673,6 +3683,10 @@ msgid ""
"channels that will be maintained at the creation of a document in the "
"system. Some examples of channels can be: Website, Phone Call, Reseller, etc."
msgstr ""
+"Controle el origen de sus iniciativas y oportunidades de venta mediante la "
+"creación de canales específicos que se usarán en la creación de documentos "
+"en el sistema. Algunos ejemplos de canales son: Sitio web, llamada "
+"telefónica, distribuidores, ..."
#. module: crm
#: selection:crm.lead2opportunity.partner,name:0
@@ -3740,7 +3754,7 @@ msgstr "Año"
#. module: crm
#: constraint:res.partner:0
msgid "Error ! You cannot create recursive associated members."
-msgstr ""
+msgstr "¡Error! No puede crear miembros asociados recursivamente."
#. module: crm
#: model:crm.case.resource.type,name:crm.type_lead8
diff --git a/addons/crm/report/crm_phonecall_report_view.xml b/addons/crm/report/crm_phonecall_report_view.xml
index a2120aef6ab..1f1a4f91046 100644
--- a/addons/crm/report/crm_phonecall_report_view.xml
+++ b/addons/crm/report/crm_phonecall_report_view.xml
@@ -91,7 +91,7 @@
crm.phonecall.report
form
tree,graph
- {"search_default_year":1,"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}
+ {"search_default_year":1,"search_default_Salesperson":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}
From this report, you can analyse the performance of your sales team, based on their phone calls. You can group or filter the information according to several criteria and drill down the information, by adding more groups in the report.
diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py
index 2473a83206c..ea96e1d16f1 100644
--- a/addons/crm/wizard/crm_lead_to_opportunity.py
+++ b/addons/crm/wizard/crm_lead_to_opportunity.py
@@ -175,12 +175,19 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory):
return res
def _convert_opportunity(self, cr, uid, ids, vals, context=None):
+ """
+ When "massively" (more than one at a time) converting leads to
+ opportunities, check the salesteam_id and salesmen_ids and update
+ the values before calling super.
+ """
+ if context is None:
+ context = {}
data = self.browse(cr, uid, ids, context=context)[0]
salesteam_id = data.section_id and data.section_id.id or False
- salesman = []
+ salesmen_ids = []
if data.user_ids:
- salesman = [x.id for x in data.user_ids]
- vals.update({'user_ids': salesman, 'section_id': salesteam_id})
+ salesmen_ids = [x.id for x in data.user_ids]
+ vals.update({'user_ids': salesmen_ids, 'section_id': salesteam_id})
return super(crm_lead2opportunity_mass_convert, self)._convert_opportunity(cr, uid, ids, vals, context=context)
def mass_convert(self, cr, uid, ids, context=None):
diff --git a/addons/crm/wizard/crm_lead_to_partner.py b/addons/crm/wizard/crm_lead_to_partner.py
index c201f484989..c48990f5866 100644
--- a/addons/crm/wizard/crm_lead_to_partner.py
+++ b/addons/crm/wizard/crm_lead_to_partner.py
@@ -35,7 +35,7 @@ class crm_lead2partner(osv.osv_memory):
}
def view_init(self, cr, uid, fields, context=None):
"""
- This function checks for precondition before wizard executes
+ Check for precondition before wizard executes.
"""
if context is None:
context = {}
@@ -69,22 +69,19 @@ class crm_lead2partner(osv.osv_memory):
return partner_id
def default_get(self, cr, uid, fields, context=None):
- """
- This function gets default values
- """
- res = super(crm_lead2partner, self).default_get(cr, uid, fields, context=context)
+ res = super(crm_lead2partner, self).default_get(cr, uid, fields, context=context)
partner_id = self._select_partner(cr, uid, context=context)
if 'partner_id' in fields:
res.update({'partner_id': partner_id})
if 'action' in fields:
res.update({'action': partner_id and 'exist' or 'create'})
-
+
return res
def open_create_partner(self, cr, uid, ids, context=None):
"""
- This function Opens form of create partner.
+ Open form of create partner.
"""
view_obj = self.pool.get('ir.ui.view')
view_id = view_obj.search(cr, uid, [('model', '=', self._name), \
@@ -101,21 +98,21 @@ class crm_lead2partner(osv.osv_memory):
def _create_partner(self, cr, uid, ids, context=None):
"""
- This function Creates partner based on action.
+ Create partner based on action.
"""
if context is None:
context = {}
lead = self.pool.get('crm.lead')
- lead_ids = context and context.get('active_ids') or []
+ lead_ids = context.get('active_ids', [])
data = self.browse(cr, uid, ids, context=context)[0]
partner_id = data.partner_id and data.partner_id.id or False
return lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context)
def make_partner(self, cr, uid, ids, context=None):
"""
- This function Makes partner based on action.
+ Make a partner based on action.
+ Only called from form view, so only meant to convert one lead at a time.
"""
- # Only called from Form view, so only meant to convert one Lead.
lead_id = context and context.get('active_id') or False
partner_ids_map = self._create_partner(cr, uid, ids, context=context)
return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_ids_map.get(lead_id, False), context=context)
diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py
index e4d5f5999cb..8308e5420d0 100644
--- a/addons/crm_claim/crm_claim.py
+++ b/addons/crm_claim/crm_claim.py
@@ -105,7 +105,7 @@ class crm_claim(base_stage, osv.osv):
'email_from': fields.char('Email', size=128, help="Destination email for email gateway."),
'partner_phone': fields.char('Phone', size=32),
'stage_id': fields.many2one ('crm.claim.stage', 'Stage',
- domain="['|', ('section_ids', '=', section_id), ('case_default', '=', True)]"),
+ domain="['&',('fold', '=', False),'|', ('section_ids', '=', section_id), ('case_default', '=', True)]"),
'cause': fields.text('Root Cause'),
'state': fields.related('stage_id', 'state', type="selection", store=True,
selection=crm.AVAILABLE_STATES, string="Status", readonly=True,
diff --git a/addons/crm_claim/crm_claim_data.xml b/addons/crm_claim/crm_claim_data.xml
index 33bd280c364..ed0558f72e9 100644
--- a/addons/crm_claim/crm_claim_data.xml
+++ b/addons/crm_claim/crm_claim_data.xml
@@ -43,38 +43,32 @@
-->
- Draft claim
+ New
draft
26
- Actions Defined
+ In Progress
open
27
- Actions Done
+ Settled
done
28
- Refused
- done
+ Rejected
+ cancel
29
-
- Cancelled
- cancel
- 30
-
-
-
+
diff --git a/addons/crm_claim/crm_claim_menu.xml b/addons/crm_claim/crm_claim_menu.xml
index 3782a3a7c00..4c0eca3d0f7 100644
--- a/addons/crm_claim/crm_claim_menu.xml
+++ b/addons/crm_claim/crm_claim_menu.xml
@@ -52,7 +52,7 @@
-
+
diff --git a/addons/crm_claim/crm_claim_view.xml b/addons/crm_claim/crm_claim_view.xml
index 2b5ca4b1556..aa4243a6838 100644
--- a/addons/crm_claim/crm_claim_view.xml
+++ b/addons/crm_claim/crm_claim_view.xml
@@ -102,16 +102,11 @@
diff --git a/addons/crm_partner_assign/i18n/es.po b/addons/crm_partner_assign/i18n/es.po
index 1f1fb3dee6f..4f6521a6421 100644
--- a/addons/crm_partner_assign/i18n/es.po
+++ b/addons/crm_partner_assign/i18n/es.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
-"PO-Revision-Date: 2011-01-16 17:13+0000\n"
-"Last-Translator: mgaja (GrupoIsep.com) \n"
+"PO-Revision-Date: 2012-11-09 12:10+0000\n"
+"Last-Translator: Pedro Manuel Baeza \n"
"Language-Team: Spanish \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-30 05:33+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-10 04:59+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,send_to:0
@@ -25,12 +25,12 @@ msgstr "Enviar a"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,subtype:0
msgid "Message type"
-msgstr ""
+msgstr "Tipo de mensaje"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,auto_delete:0
msgid "Permanently delete emails after sending"
-msgstr ""
+msgstr "Eliminar permanentemente los emails depués de su envío"
#. module: crm_partner_assign
#: field:crm.lead.report.assign,delay_close:0
@@ -40,7 +40,7 @@ msgstr "Retraso para cerrar"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,email_to:0
msgid "Message recipients"
-msgstr ""
+msgstr "Destinatarios del mensaje"
#. module: crm_partner_assign
#: field:crm.lead.report.assign,planned_revenue:0
@@ -61,7 +61,7 @@ msgstr "Agrupar por..."
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,template_id:0
msgid "Template"
-msgstr ""
+msgstr "Plantilla"
#. module: crm_partner_assign
#: view:crm.lead:0
@@ -76,12 +76,12 @@ msgstr "Geo localizar"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,body:0
msgid "Plain-text version of the message"
-msgstr ""
+msgstr "Versión en texto plano del mensaje"
#. module: crm_partner_assign
#: view:crm.lead.forward.to.partner:0
msgid "Body"
-msgstr ""
+msgstr "Cuerpo"
#. module: crm_partner_assign
#: selection:crm.lead.report.assign,month:0
@@ -101,7 +101,7 @@ msgstr "Demora cierre"
#. module: crm_partner_assign
#: view:crm.partner.report.assign:0
msgid "#Partner"
-msgstr ""
+msgstr "Nº empresa"
#. module: crm_partner_assign
#: selection:crm.lead.forward.to.partner,history:0
@@ -137,7 +137,7 @@ msgstr "Más alta"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,body:0
msgid "Text contents"
-msgstr ""
+msgstr "Contenido del texto"
#. module: crm_partner_assign
#: view:crm.lead.report.assign:0
@@ -148,7 +148,7 @@ msgstr "Día"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,message_id:0
msgid "Message unique identifier"
-msgstr ""
+msgstr "Identificador único del mensaje"
#. module: crm_partner_assign
#: selection:crm.lead.forward.to.partner,history:0
@@ -167,6 +167,8 @@ msgid ""
"Add here all attachments of the current document you want to include in the "
"Email."
msgstr ""
+"Añada aquí todos los datos adjuntos del documento que quiere incluir en el "
+"correo."
#. module: crm_partner_assign
#: selection:crm.lead.report.assign,state:0
@@ -195,17 +197,17 @@ msgstr ""
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,body_html:0
msgid "Rich-text/HTML version of the message"
-msgstr ""
+msgstr "Versión en texto enriquecido / HTML del mensaje"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,auto_delete:0
msgid "Auto Delete"
-msgstr ""
+msgstr "Auto eliminar"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,email_bcc:0
msgid "Blind carbon copy message recipients"
-msgstr ""
+msgstr "Destinatarios de la copia oculta"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,partner_id:0
@@ -254,7 +256,7 @@ msgstr "Sección"
#. module: crm_partner_assign
#: view:crm.lead.forward.to.partner:0
msgid "Send"
-msgstr ""
+msgstr "Enviar"
#. module: crm_partner_assign
#: view:res.partner:0
@@ -286,7 +288,7 @@ msgstr "Tipo"
#. module: crm_partner_assign
#: view:crm.partner.report.assign:0
msgid "Name"
-msgstr ""
+msgstr "Nombre"
#. module: crm_partner_assign
#: selection:crm.lead.report.assign,priority:0
@@ -299,11 +301,13 @@ msgid ""
"Type of message, usually 'html' or 'plain', used to select plaintext or rich "
"text contents accordingly"
msgstr ""
+"Tipo de mensaje, normalmente 'html' o 'plano', utilizado para seleccionar "
+"contenidos en texto plano o en texto enriquecido"
#. module: crm_partner_assign
#: view:crm.lead.report.assign:0
msgid "Assign Date"
-msgstr ""
+msgstr "Fecha de asignación"
#. module: crm_partner_assign
#: view:crm.lead.report.assign:0
@@ -318,7 +322,7 @@ msgstr "Fecha creación"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,res_id:0
msgid "Related Document ID"
-msgstr ""
+msgstr "ID del docuemtno relacionado"
#. module: crm_partner_assign
#: view:crm.lead.report.assign:0
@@ -349,7 +353,7 @@ msgstr "Etapa"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,model:0
msgid "Related Document model"
-msgstr ""
+msgstr "Modelo del documento relacionado"
#. module: crm_partner_assign
#: code:addons/crm_partner_assign/wizard/crm_forward_to_partner.py:192
@@ -392,7 +396,7 @@ msgstr "Cerrar"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,use_template:0
msgid "Use Template"
-msgstr ""
+msgstr "Usar plantilla"
#. module: crm_partner_assign
#: model:ir.actions.act_window,name:crm_partner_assign.action_report_crm_opportunity_assign
@@ -464,12 +468,12 @@ msgstr "nº oportunidades"
#. module: crm_partner_assign
#: view:crm.lead:0
msgid "Team"
-msgstr ""
+msgstr "Equipo"
#. module: crm_partner_assign
#: view:crm.lead:0
msgid "Referred Partner"
-msgstr ""
+msgstr "Empresa referida"
#. module: crm_partner_assign
#: selection:crm.lead.report.assign,state:0
@@ -490,7 +494,7 @@ msgstr "Cerrado"
#. module: crm_partner_assign
#: model:ir.actions.act_window,name:crm_partner_assign.action_crm_send_mass_forward
msgid "Mass forward to partner"
-msgstr ""
+msgstr "Envío masivo a empresa"
#. module: crm_partner_assign
#: view:res.partner:0
@@ -570,7 +574,7 @@ msgstr "Longitud Geo"
#. module: crm_partner_assign
#: field:crm.partner.report.assign,opp:0
msgid "# of Opportunity"
-msgstr ""
+msgstr "Nº oportunidad"
#. module: crm_partner_assign
#: view:crm.lead.report.assign:0
@@ -600,12 +604,12 @@ msgstr "Empresa a la que este caso ha sido reenviado/asignado."
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,date:0
msgid "Date"
-msgstr ""
+msgstr "Fecha"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,body_html:0
msgid "Rich-text contents"
-msgstr ""
+msgstr "Contenido en texto enriquecido"
#. module: crm_partner_assign
#: view:crm.lead.report.assign:0
@@ -620,18 +624,18 @@ msgstr "res.empresa.nivel"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,message_id:0
msgid "Message-Id"
-msgstr ""
+msgstr "Id del mensaje"
#. module: crm_partner_assign
#: view:crm.lead.forward.to.partner:0
#: field:crm.lead.forward.to.partner,attachment_ids:0
msgid "Attachments"
-msgstr ""
+msgstr "Adjuntos"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,email_cc:0
msgid "Cc"
-msgstr ""
+msgstr "Cc"
#. module: crm_partner_assign
#: selection:crm.lead.report.assign,month:0
@@ -641,7 +645,7 @@ msgstr "Septiembre"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,references:0
msgid "References"
-msgstr ""
+msgstr "Referencias"
#. module: crm_partner_assign
#: view:crm.lead.report.assign:0
@@ -668,7 +672,7 @@ msgstr "Abierto"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,email_cc:0
msgid "Carbon copy message recipients"
-msgstr ""
+msgstr "Destinatarios de la copia"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,headers:0
@@ -676,6 +680,8 @@ msgid ""
"Full message headers, e.g. SMTP session headers (usually available on "
"inbound messages only)"
msgstr ""
+"Cabeceras completas del mensaje, por ejemplo las cabeceras de sesión SMTP "
+"(normalmente disponibles sólo en mensajes entrantes)"
#. module: crm_partner_assign
#: field:res.partner,date_localization:0
@@ -698,11 +704,13 @@ msgid ""
"Message sender, taken from user preferences. If empty, this is not a mail "
"but a message."
msgstr ""
+"Remitente del mensaje, proveniente de las preferencias del usuario. Si está "
+"vacío, esto no es correo electrónico, sino un mensaje."
#. module: crm_partner_assign
#: field:crm.partner.report.assign,nbr:0
msgid "# of Partner"
-msgstr ""
+msgstr "Nº empresa"
#. module: crm_partner_assign
#: view:crm.lead.forward.to.partner:0
@@ -713,7 +721,7 @@ msgstr "Reenviar a empresa"
#. module: crm_partner_assign
#: field:crm.partner.report.assign,name:0
msgid "Partner name"
-msgstr ""
+msgstr "Nombre de la empresa"
#. module: crm_partner_assign
#: selection:crm.lead.report.assign,month:0
@@ -728,7 +736,7 @@ msgstr "Ingreso estimado"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,reply_to:0
msgid "Reply-To"
-msgstr ""
+msgstr "Responder a"
#. module: crm_partner_assign
#: field:crm.lead,partner_assigned_id:0
@@ -748,7 +756,7 @@ msgstr "Oportunidad"
#. module: crm_partner_assign
#: view:crm.lead.forward.to.partner:0
msgid "Send Mail"
-msgstr ""
+msgstr "Enviar correo"
#. module: crm_partner_assign
#: field:crm.lead.report.assign,partner_id:0
@@ -776,7 +784,7 @@ msgstr "País"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,headers:0
msgid "Message headers"
-msgstr ""
+msgstr "Cabeceras del mensaje"
#. module: crm_partner_assign
#: view:res.partner:0
@@ -786,7 +794,7 @@ msgstr "Convertir en oportunidad"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,email_bcc:0
msgid "Bcc"
-msgstr ""
+msgstr "Cco"
#. module: crm_partner_assign
#: view:crm.lead:0
@@ -802,7 +810,7 @@ msgstr "Abril"
#: model:ir.actions.act_window,name:crm_partner_assign.action_report_crm_partner_assign
#: model:ir.ui.menu,name:crm_partner_assign.menu_report_crm_partner_assign_tree
msgid "Partnership Analysis"
-msgstr ""
+msgstr "Análisis de la relación"
#. module: crm_partner_assign
#: model:ir.model,name:crm_partner_assign.model_crm_lead
@@ -817,7 +825,7 @@ msgstr "Pendiente"
#. module: crm_partner_assign
#: view:crm.partner.report.assign:0
msgid "Partner assigned Analysis"
-msgstr ""
+msgstr "Análisis de la empresa asignada"
#. module: crm_partner_assign
#: model:ir.model,name:crm_partner_assign.model_crm_lead_report_assign
@@ -828,11 +836,12 @@ msgstr "Informe de iniciativas CRM"
#: help:crm.lead.forward.to.partner,references:0
msgid "Message references, such as identifiers of previous messages"
msgstr ""
+"Referencias del mensaje, tales como identificadores de mensajes anteriores"
#. module: crm_partner_assign
#: constraint:res.partner:0
msgid "Error ! You cannot create recursive associated members."
-msgstr ""
+msgstr "¡Error! No puede crear miembros asociados recursivamente."
#. module: crm_partner_assign
#: selection:crm.lead.forward.to.partner,history:0
@@ -847,12 +856,12 @@ msgstr "Secuencia"
#. module: crm_partner_assign
#: model:ir.model,name:crm_partner_assign.model_crm_partner_report_assign
msgid "CRM Partner Report"
-msgstr ""
+msgstr "Informe de la empresa CRM"
#. module: crm_partner_assign
#: model:ir.model,name:crm_partner_assign.model_crm_lead_forward_to_partner
msgid "Email composition wizard"
-msgstr ""
+msgstr "Asistente de composición de e-mail"
#. module: crm_partner_assign
#: selection:crm.lead.report.assign,priority:0
@@ -873,7 +882,7 @@ msgstr "Fecha de creación"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,filter_id:0
msgid "Filters"
-msgstr ""
+msgstr "Filtros"
#. module: crm_partner_assign
#: view:crm.lead.report.assign:0
@@ -884,7 +893,7 @@ msgstr "Año"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,reply_to:0
msgid "Preferred response address for the message"
-msgstr ""
+msgstr "Dirección de correo de respuesta preferida para este mensaje"
#~ msgid "Reply-to of the Sales team defined on this case"
#~ msgstr "\"Responder a\" del equipo de ventas definido en este caso"
@@ -941,3 +950,6 @@ msgstr ""
#~ "partners o asesores,\n"
#~ "basándose en geo-localización.\n"
#~ " "
+
+#~ msgid "E-mail composition wizard"
+#~ msgstr "Asistente de composición de e-mail"
diff --git a/addons/crm_profiling/crm_profiling.py b/addons/crm_profiling/crm_profiling.py
index 8f07dfcbdda..7dc72f80289 100644
--- a/addons/crm_profiling/crm_profiling.py
+++ b/addons/crm_profiling/crm_profiling.py
@@ -244,6 +244,7 @@ class crm_segmentation(osv.osv):
@param uid: the current user’s ID for security checks,
@param ids: List of crm segmentation’s IDs """
+ partner_obj = self.pool.get('res.partner')
categs = self.read(cr,uid,ids,['categ_id','exclusif','partner_id', \
'sales_purchase_active', 'profiling_active'])
for categ in categs:
@@ -280,8 +281,10 @@ class crm_segmentation(osv.osv):
for pid in to_remove_list:
partners.remove(pid)
- for partner_id in partners:
- cr.execute('insert into res_partner_res_partner_category_rel (category_id,partner_id) values (%s,%s)', (categ['categ_id'][0],partner_id))
+ for partner in partner_obj.browse(cr, uid, partners):
+ category_ids = [categ_id.id for categ_id in partner.category_id]
+ if categ['categ_id'][0] not in category_ids:
+ cr.execute('insert into res_partner_res_partner_category_rel (category_id,partner_id) values (%s,%s)', (categ['categ_id'][0],partner.id))
self.write(cr, uid, [id], {'state':'not running', 'partner_id':0})
return True
diff --git a/addons/document/nodes.py b/addons/document/nodes.py
index 6bcb2237a9e..aeb2e64fc97 100644
--- a/addons/document/nodes.py
+++ b/addons/document/nodes.py
@@ -823,6 +823,7 @@ class node_res_dir(node_class):
uid = self.context.uid
ctx = self.context.context.copy()
ctx.update(self.dctx)
+ ctx.update(self.context.extra_ctx)
where = []
if self.domain:
app = safe_eval(self.domain, ctx)
diff --git a/addons/document_ftp/ftpserver/abstracted_fs.py b/addons/document_ftp/ftpserver/abstracted_fs.py
index dbda715f614..326c62f7a92 100644
--- a/addons/document_ftp/ftpserver/abstracted_fs.py
+++ b/addons/document_ftp/ftpserver/abstracted_fs.py
@@ -272,7 +272,7 @@ class abstracted_fs(object):
if path.startswith('/'):
path = path[1:]
- p_parts = path.split('/') # hard-code the unix sep here, by spec.
+ p_parts = path.split(os.sep)
assert '..' not in p_parts
diff --git a/addons/document_page/wizard/document_page_show_diff.py b/addons/document_page/wizard/document_page_show_diff.py
index ab5572768f4..13e8e79b3b7 100644
--- a/addons/document_page/wizard/document_page_show_diff.py
+++ b/addons/document_page/wizard/document_page_show_diff.py
@@ -43,7 +43,7 @@ class showdiff(osv.osv_memory):
elif len(ids) == 1:
old = history.browse(cr, uid, ids[0])
- nids = history.search(cr, uid, [('document_id', '=', old.document_id.id)])
+ nids = history.search(cr, uid, [('page_id', '=', old.page_id.id)])
nids.sort()
diff = history.getDiff(cr, uid, ids[0], nids[-1])
else:
diff --git a/addons/document_webdav/document_webdav.py b/addons/document_webdav/document_webdav.py
index 3b604d6cffb..cb1315140a3 100644
--- a/addons/document_webdav/document_webdav.py
+++ b/addons/document_webdav/document_webdav.py
@@ -51,9 +51,10 @@ class document_davdir(osv.osv):
# that might be not worth preparing.
nctx.extra_ctx['webdav_path'] = '/'+config.get_misc('webdav','vdir','webdav')
usr_obj = self.pool.get('res.users')
- res = usr_obj.read(cr, uid, uid, ['login'])
+ res = usr_obj.read(cr, uid, uid, ['login','lang'])
if res:
nctx.extra_ctx['username'] = res['login']
+ nctx.extra_ctx['lang'] = res['lang']
# TODO group
return
diff --git a/addons/edi/static/src/js/edi.js b/addons/edi/static/src/js/edi.js
index e82871cdd43..a9c6981c6be 100644
--- a/addons/edi/static/src/js/edi.js
+++ b/addons/edi/static/src/js/edi.js
@@ -15,7 +15,7 @@ openerp.edi.EdiView = openerp.web.Widget.extend({
this._super();
var self = this;
var param = {"db": self.db, "token": self.token};
- return self.rpc('/edi/get_edi_document', param).then(this.on_document_loaded, this.on_document_failed);
+ return self.rpc('/edi/get_edi_document', param).done(this.on_document_loaded).fail(this.on_document_failed);
},
on_document_loaded: function(docs){
this.doc = docs[0];
@@ -108,7 +108,7 @@ openerp.edi.EdiView = openerp.web.Widget.extend({
});
openerp.edi.edi_view = function (db, token) {
- openerp.session.session_bind().then(function () {
+ openerp.session.session_bind().done(function () {
new openerp.edi.EdiView(null,db,token).appendTo($("body").addClass('openerp'));
});
}
@@ -149,11 +149,11 @@ openerp.edi.EdiImport = openerp.web.Widget.extend({
},
do_import: function() {
- this.rpc('/edi/import_edi_url', {url: this.url}).then(this.on_imported, this.on_imported_error);
+ this.rpc('/edi/import_edi_url', {url: this.url}).done(this.on_imported).fail(this.on_imported_error);
},
on_imported: function(response) {
if ('action' in response) {
- this.rpc("/web/session/save_session_action", {the_action: response.action}).then(function(key) {
+ this.rpc("/web/session/save_session_action", {the_action: response.action}).done(function(key) {
window.location = "/#sa="+encodeURIComponent(key);
});
}
@@ -188,7 +188,7 @@ openerp.edi.EdiImport = openerp.web.Widget.extend({
});
openerp.edi.edi_import = function (url) {
- openerp.session.session_bind().then(function () {
+ openerp.session.session_bind().done(function () {
new openerp.edi.EdiImport(null,url).appendTo($("body").addClass('openerp'));
});
}
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index 73337d92ab0..fbef2b016ac 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -118,7 +118,8 @@ class email_template(osv.osv):
"of the message"),
'subject': fields.char('Subject', translate=True, help="Subject (placeholders may be used here)",),
'email_from': fields.char('From', help="Sender address (placeholders may be used here)"),
- 'email_to': fields.char('To', help="Comma-separated recipient addresses (placeholders may be used here)"),
+ 'email_to': fields.char('To (Emails)', help="Comma-separated recipient addresses (placeholders may be used here)"),
+ 'email_recipients': fields.char('To (Partners)', help="Comma-separated ids of recipient partners (placeholders may be used here)"),
'email_cc': fields.char('Cc', help="Carbon copy recipients (placeholders may be used here)"),
'reply_to': fields.char('Reply-To', help="Preferred response address (placeholders may be used here)"),
'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False,
@@ -286,7 +287,7 @@ class email_template(osv.osv):
template = self.get_email_template(cr, uid, template_id, res_id, context)
values = {}
for field in ['subject', 'body_html', 'email_from',
- 'email_to', 'email_cc', 'reply_to']:
+ 'email_to', 'email_recipients', 'email_cc', 'reply_to']:
values[field] = self.render_template(cr, uid, getattr(template, field),
template.model, res_id, context=context) \
or False
diff --git a/addons/email_template/email_template_view.xml b/addons/email_template/email_template_view.xml
index 448655a3121..4cddb31c8e1 100644
--- a/addons/email_template/email_template_view.xml
+++ b/addons/email_template/email_template_view.xml
@@ -29,7 +29,8 @@
-
+
+
@@ -77,6 +78,7 @@
+
diff --git a/addons/email_template/tests/test_mail.py b/addons/email_template/tests/test_mail.py
index 7af05f38b0e..9a7b71a4e42 100644
--- a/addons/email_template/tests/test_mail.py
+++ b/addons/email_template/tests/test_mail.py
@@ -63,11 +63,16 @@ class test_message_compose(test_mail.TestMailMockups):
# Create template on mail.group, with attachments
group_model_id = self.registry('ir.model').search(cr, uid, [('model', '=', 'mail.group')])[0]
email_template = self.registry('email.template')
- email_template_id = email_template.create(cr, uid, {'model_id': group_model_id,
- 'name': 'Pigs Template', 'subject': '${object.name}',
- 'body_html': '${object.description}', 'user_signature': True,
+ email_template_id = email_template.create(cr, uid, {
+ 'model_id': group_model_id,
+ 'name': 'Pigs Template',
+ 'subject': '${object.name}',
+ 'body_html': '${object.description}',
+ 'user_signature': True,
'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])],
- 'email_to': 'b@b.b c@c.c', 'email_cc': 'd@d.d'})
+ 'email_to': 'b@b.b c@c.c',
+ 'email_cc': 'd@d.d'
+ })
# ----------------------------------------
# CASE1: comment and save as template
@@ -76,9 +81,9 @@ class test_message_compose(test_mail.TestMailMockups):
# 1. Comment on pigs
compose_id = mail_compose.create(cr, uid,
{'subject': 'Forget me subject', 'body': 'Dummy body
'},
- {'default_composition_mode': 'comment', 'default_model': 'mail.group',
+ {'default_composition_mode': 'comment',
+ 'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
- 'default_template_id': email_template_id,
'active_ids': [self.group_pigs_id, self.group_bird_id]})
compose = mail_compose.browse(cr, uid, compose_id)
@@ -97,8 +102,10 @@ class test_message_compose(test_mail.TestMailMockups):
# 1. Comment on pigs
compose_id = mail_compose.create(cr, uid,
{'subject': 'Forget me subject', 'body': 'Dummy body'},
- {'default_composition_mode': 'comment', 'default_model': 'mail.group',
+ {'default_composition_mode': 'comment',
+ 'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
+ 'default_use_template': False,
'default_template_id': email_template_id,
'active_ids': [self.group_pigs_id, self.group_bird_id]})
compose = mail_compose.browse(cr, uid, compose_id)
@@ -135,8 +142,10 @@ class test_message_compose(test_mail.TestMailMockups):
# 1. Mass_mail on pigs and bird, with a default_partner_ids set to check he is correctly added
compose_id = mail_compose.create(cr, uid,
{'subject': 'Forget me subject', 'body': 'Dummy body'},
- {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group',
+ {'default_composition_mode': 'mass_mail',
+ 'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
+ 'default_use_template': False,
'default_template_id': email_template_id,
'default_partner_ids': [p_a_id],
'active_ids': [self.group_pigs_id, self.group_bird_id]})
@@ -170,3 +179,26 @@ class test_message_compose(test_mail.TestMailMockups):
partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])])
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')
+
+ # ----------------------------------------
+ # CASE4: test newly introduced email_recipients field
+ # ----------------------------------------
+
+ # get already-created partners back
+ p_b_id = self.res_partner.search(cr, uid, [('email', '=', 'b@b.b')])[0]
+ p_c_id = self.res_partner.search(cr, uid, [('email', '=', 'c@c.c')])[0]
+ p_d_id = self.res_partner.search(cr, uid, [('email', '=', 'd@d.d')])[0]
+ # modify template: use email_recipients, use template and email address in email_to to test all features together
+ user_model_id = self.registry('ir.model').search(cr, uid, [('model', '=', 'res.users')])[0]
+ email_template.write(cr, uid, [email_template_id], {
+ 'model_id': user_model_id,
+ 'body_html': '${object.login}',
+ 'email_to': '${object.email} c@c',
+ 'email_recipients': '%i,%i' % (p_b_id, p_c_id),
+ 'email_cc': 'd@d',
+ })
+ # patner by email + partner by id (no double)
+ send_to = [p_a_id, p_b_id, p_c_id, p_d_id]
+ # Generate messsage with default email and partner on template
+ mail_value = mail_compose.generate_email_for_composer(cr, uid, email_template_id, uid)
+ self.assertEqual(set(mail_value['partner_ids']), set(send_to), 'mail.message partner_ids list created by template is incorrect')
diff --git a/addons/email_template/wizard/email_template_preview_view.xml b/addons/email_template/wizard/email_template_preview_view.xml
index f91535a17a5..8dd4bc92ba7 100644
--- a/addons/email_template/wizard/email_template_preview_view.xml
+++ b/addons/email_template/wizard/email_template_preview_view.xml
@@ -17,6 +17,7 @@
+
diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py
index fe6f85da544..129c32722f5 100644
--- a/addons/email_template/wizard/mail_compose_message.py
+++ b/addons/email_template/wizard/mail_compose_message.py
@@ -52,8 +52,9 @@ class mail_compose_message(osv.TransientModel):
context = {}
result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)
result['template_id'] = context.get('default_template_id', context.get('mail.compose.template_id', False))
+
# pre-render the template if any
- if result.get('use_template'):
+ if result.get('use_template') and result.get('template_id'):
onchange_res = self.onchange_use_template(cr, uid, [], result.get('use_template'), result.get('template_id'),
result.get('composition_mode'), result.get('model'), result.get('res_id'), context=context)
result.update(onchange_res['value'])
@@ -65,6 +66,10 @@ class mail_compose_message(osv.TransientModel):
'template_id': fields.selection(_get_templates, 'Template', size=-1),
}
+ _defaults = {
+ 'use_template': True,
+ }
+
def onchange_template_id(self, cr, uid, ids, use_template, template_id, composition_mode, model, res_id, context=None):
""" - use_template not set: return default_get
- use_template set in mass_mailing: we cannot render, so return the template values
@@ -148,15 +153,23 @@ class mail_compose_message(osv.TransientModel):
mail.compose.message, transform email_cc and email_to into partner_ids """
template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context)
# filter template values
- fields = ['body', 'body_html', 'subject', 'email_to', 'email_cc', 'attachments']
+ fields = ['body', 'body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachments']
values = dict((field, template_values[field]) for field in fields if template_values.get(field))
values['body'] = values.pop('body_html', '')
# transform email_to, email_cc into partner_ids
values['partner_ids'] = []
+
mails = tools.email_split(values.pop('email_to', '') + ' ' + values.pop('email_cc', ''))
for mail in mails:
partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context)
values['partner_ids'].append(partner_id)
+ email_recipients = values.pop('email_recipients', '')
+ if email_recipients:
+ for partner_id in email_recipients.split(','):
+ values['partner_ids'].append(int(partner_id))
+
+ values['partner_ids'] = list(set(values['partner_ids']))
+
return values
def render_message(self, cr, uid, wizard, res_id, context=None):
diff --git a/addons/email_template/wizard/mail_compose_message_view.xml b/addons/email_template/wizard/mail_compose_message_view.xml
index 05d0e8dffa1..0a530299d4a 100644
--- a/addons/email_template/wizard/mail_compose_message_view.xml
+++ b/addons/email_template/wizard/mail_compose_message_view.xml
@@ -8,23 +8,23 @@
-
-
-
-
-
+
+
-
-
-
+
+
+ Use template
+
+
+ or
+
+
diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml
index 2895855fc2e..85e3020512e 100644
--- a/addons/event/event_view.xml
+++ b/addons/event/event_view.xml
@@ -510,7 +510,7 @@
event.registration
-
+
@@ -519,6 +519,7 @@
+
diff --git a/addons/event_sale/event_sale.py b/addons/event_sale/event_sale.py
index 2dd40a7c285..f4d7fcaa013 100644
--- a/addons/event_sale/event_sale.py
+++ b/addons/event_sale/event_sale.py
@@ -39,7 +39,7 @@ class sale_order_line(osv.osv):
_columns = {
'event_id': fields.many2one('event.event', 'Event', help="Choose an event and it will automatically create a registration for this event."),
#those 2 fields are used for dynamic domains and filled by onchange
- 'event_type_id': fields.related('event_type_id', type='many2one', relation="event.type", string="Event Type"),
+ 'event_type_id': fields.related('product_id','event_type_id', type='many2one', relation="event.type", string="Event Type"),
'event_ok': fields.related('product_id', 'event_ok', string='event_ok', type='boolean'),
}
diff --git a/addons/google_docs/static/src/js/gdocs.js b/addons/google_docs/static/src/js/gdocs.js
index 0cc93b2510c..45e1d09d42c 100644
--- a/addons/google_docs/static/src/js/gdocs.js
+++ b/addons/google_docs/static/src/js/gdocs.js
@@ -16,9 +16,9 @@ var _t = instance.web._t,
var view = self.getParent();
var ids = ( view.fields_view.type != "form" )? view.groups.get_selection().ids : [ view.datarecord.id ];
if( !_.isEmpty(ids) ){
- view.sidebar_context().then(function (context) {
+ view.sidebar_context().done(function (context) {
var ds = new instance.web.DataSet(this, 'ir.attachment', context);
- ds.call('google_doc_get', [view.dataset.model, ids, context]).then(function(r) {
+ ds.call('google_doc_get', [view.dataset.model, ids, context]).done(function(r) {
if (r == 'False') {
var params = {
error: response,
diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml
index c4a622d7a4f..8918c5012c3 100644
--- a/addons/hr/hr_view.xml
+++ b/addons/hr/hr_view.xml
@@ -288,7 +288,7 @@
- Categories of Employee
+ Categories of Employees
hr.employee.category
form
tree,form
@@ -342,8 +342,8 @@
-
+
diff --git a/addons/hr/i18n/es_EC.po b/addons/hr/i18n/es_EC.po
index 32e81b6d14d..bfbbb7fa180 100644
--- a/addons/hr/i18n/es_EC.po
+++ b/addons/hr/i18n/es_EC.po
@@ -8,15 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
"POT-Creation-Date: 2012-02-08 01:37+0100\n"
-"PO-Revision-Date: 2012-01-03 02:55+0000\n"
-"Last-Translator: Christopher Ormaza - (Ecuadorenlinea.net) "
-"\n"
+"PO-Revision-Date: 2012-11-10 17:20+0000\n"
+"Last-Translator: Cristian Salamea (Gnuthink) \n"
"Language-Team: Spanish (Ecuador) \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-30 05:17+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-11 04:57+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
#. module: hr
#: model:process.node,name:hr.process_node_openerpuser0
@@ -199,6 +198,8 @@ msgstr "Mujer"
msgid ""
"Expected number of employees for this job position after new recruitment."
msgstr ""
+"Número de Empleados para este puesto de trabajo después de las nuevas "
+"contrataciones"
#. module: hr
#: model:ir.ui.menu,name:hr.menu_open_view_attendance_reason_new_config
@@ -291,7 +292,7 @@ msgstr "Categorías"
#. module: hr
#: field:hr.job,expected_employees:0
msgid "Total Employees"
-msgstr ""
+msgstr "Total de Empleados"
#. module: hr
#: selection:hr.employee,marital:0
@@ -435,7 +436,7 @@ msgstr "Estado"
#: model:ir.actions.act_window,name:hr.open_view_categ_tree
#: model:ir.ui.menu,name:hr.menu_view_employee_category_tree
msgid "Categories Structure"
-msgstr ""
+msgstr "Estructura de categorías"
#. module: hr
#: field:hr.employee,partner_id:0
@@ -465,7 +466,7 @@ msgstr "¡Error! No se puede crear una jerarquía recursiva de empleados."
#. module: hr
#: model:ir.actions.act_window,name:hr.action2
msgid "Subordinate Hierarchy"
-msgstr ""
+msgstr "Jerarquía subirdinada"
#. module: hr
#: model:ir.actions.act_window,help:hr.view_department_form_installer
@@ -715,12 +716,12 @@ msgstr "Subordinados"
#. module: hr
#: field:hr.job,no_of_employee:0
msgid "Number of employees currently occupying this job position."
-msgstr ""
+msgstr "Número de empleados ocupando actualmente este puesto de trabajo"
#. module: hr
#: field:hr.job,no_of_recruitment:0
msgid "Number of new employees you expect to recruit."
-msgstr ""
+msgstr "Número de empleados que espera contratar"
#~ msgid "Working Time Categories"
#~ msgstr "Categorías de Horarios de Trabajo"
diff --git a/addons/hr_attendance/report/attendance_by_month.py b/addons/hr_attendance/report/attendance_by_month.py
index f104b9c287a..6d41869ff23 100644
--- a/addons/hr_attendance/report/attendance_by_month.py
+++ b/addons/hr_attendance/report/attendance_by_month.py
@@ -31,6 +31,7 @@ from report.interface import toxml
from report import report_sxw
from tools import ustr
from tools.translate import _
+from tools import to_xml
one_day = relativedelta(days=1)
month2name = [0, 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
@@ -111,7 +112,7 @@ class report_custom(report_rml):
%s
%s
- ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,uid).company_id.name)
+ ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),to_xml(pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,uid).company_id.name))
first_date = str(month)
som = datetime.strptime(first_date, '%Y-%m-%d %H:%M:%S')
diff --git a/addons/hr_attendance/static/src/js/attendance.js b/addons/hr_attendance/static/src/js/attendance.js
index d92e7534786..84516353b9d 100644
--- a/addons/hr_attendance/static/src/js/attendance.js
+++ b/addons/hr_attendance/static/src/js/attendance.js
@@ -57,7 +57,7 @@ openerp.hr_attendance = function (instance) {
var employee = new instance.web.DataSetSearch(self, 'hr.employee', self.session.user_context, [
['user_id', '=', self.session.uid]
]);
- return employee.read_slice(['id', 'name', 'state', 'last_sign', 'attendance_access']).pipe(function (res) {
+ return employee.read_slice(['id', 'name', 'state', 'last_sign', 'attendance_access']).then(function (res) {
if (_.isEmpty(res) )
return;
if (res[0].attendance_access == false){
@@ -75,7 +75,7 @@ openerp.hr_attendance = function (instance) {
do_update: function () {
this._super();
var self = this;
- this.update_promise = this.update_promise.then(function () {
+ this.update_promise = this.update_promise.done(function () {
if (self.attendanceslider)
return;
self.attendanceslider = new instance.hr_attendance.AttendanceSlider(self);
diff --git a/addons/hr_evaluation/hr_evaluation.py b/addons/hr_evaluation/hr_evaluation.py
index b13e90cd627..eece0c7497e 100644
--- a/addons/hr_evaluation/hr_evaluation.py
+++ b/addons/hr_evaluation/hr_evaluation.py
@@ -291,6 +291,7 @@ survey_request()
class hr_evaluation_interview(osv.osv):
_name = 'hr.evaluation.interview'
_inherits = {'survey.request': 'request_id'}
+ _inherit = 'mail.thread'
_rec_name = 'request_id'
_description = 'Appraisal Interview'
_columns = {
diff --git a/addons/hr_evaluation/hr_evaluation_view.xml b/addons/hr_evaluation/hr_evaluation_view.xml
index 697ed6bacea..e0978847acb 100644
--- a/addons/hr_evaluation/hr_evaluation_view.xml
+++ b/addons/hr_evaluation/hr_evaluation_view.xml
@@ -266,7 +266,7 @@
Each employee may be assigned an Appraisal Plan. Such a plan
defines the frequency and the way you manage your periodic
personnel evaluation. You will be able to define steps and
- attach interviews to each step. OpenERP manages all kind of
+ attach interviews to each step. OpenERP manages all kinds of
evaluations: bottom-up, top-down, self-evaluation and final
evaluation by the manager.
diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py
index c9961fa8a1d..ccb8af68e21 100644
--- a/addons/hr_expense/hr_expense.py
+++ b/addons/hr_expense/hr_expense.py
@@ -64,22 +64,22 @@ class hr_expense_expense(osv.osv):
_description = "Expense"
_order = "id desc"
_columns = {
- 'name': fields.char('Description', size=128),
+ 'name': fields.char('Description', size=128, required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'id': fields.integer('Sheet ID', readonly=True),
- 'date': fields.date('Date', select=True),
+ 'date': fields.date('Date', select=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'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),
+ 'employee_id': fields.many2one('hr.employee', "Employee", required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'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 By'),
+ 'user_valid': fields.many2one('res.users', 'Validation By', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'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'),
'amount': fields.function(_amount, string='Total Amount', digits_compute= dp.get_precision('Account')),
'voucher_id': fields.many2one('account.voucher', "Employee's Receipt"),
- 'currency_id': fields.many2one('res.currency', 'Currency', required=True),
- 'department_id':fields.many2one('hr.department','Department'),
+ 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
+ 'department_id':fields.many2one('hr.department','Department', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'company_id': fields.many2one('res.company', 'Company', required=True),
'state': fields.selection([
('draft', 'New'),
@@ -100,6 +100,12 @@ class hr_expense_expense(osv.osv):
'currency_id': _get_currency,
}
+ def unlink(self, cr, uid, ids, context=None):
+ for rec in self.browse(cr, uid, ids, context=context):
+ if rec.state != 'draft':
+ raise osv.except_osv(_('Warning!'),_('You can only delete draft expenses!'))
+ return super(hr_expense_expense, self).unlink(cr, uid, ids, context)
+
def onchange_currency_id(self, cr, uid, ids, currency_id=False, company_id=False, context=None):
res = {'value': {'journal_id': False}}
journal_ids = self.pool.get('account.journal').search(cr, uid, [('type','=','purchase'), ('currency','=',currency_id), ('company_id', '=', company_id)], context=context)
@@ -236,16 +242,6 @@ class product_product(osv.osv):
'hr_expense_ok': fields.boolean('Can be Expensed', help="Specify if the product can be selected in an HR expense line."),
}
- def on_change_hr_expense_ok(self, cr, uid, id, hr_expense_ok):
-
- if not hr_expense_ok:
- return {}
- data_obj = self.pool.get('ir.model.data')
- cat_id = data_obj._get_id(cr, uid, 'hr_expense', 'cat_expense')
- categ_id = data_obj.browse(cr, uid, cat_id).res_id
- res = {'value' : {'type':'service','sale_ok' :False,'categ_id':categ_id }}
- return res
-
product_product()
class hr_expense_line(osv.osv):
diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml
index 74b77009aae..d4b7cbae1ce 100644
--- a/addons/hr_expense/hr_expense_view.xml
+++ b/addons/hr_expense/hr_expense_view.xml
@@ -195,7 +195,7 @@
-
+
diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py
index f315f3d4ef3..92b1d57ce15 100644
--- a/addons/hr_holidays/hr_holidays.py
+++ b/addons/hr_holidays/hr_holidays.py
@@ -90,6 +90,18 @@ class hr_holidays_status(osv.osv):
'color_name': 'red',
'active': True,
}
+
+ def name_get(self, cr, uid, ids, context=None):
+ if not ids:
+ return []
+ res = []
+ for record in self.browse(cr, uid, ids, context=context):
+ name = record.name
+ if not record.limit:
+ name = name + (' (%d/%d)' % (record.leaves_taken or 0.0, record.max_leaves or 0.0))
+ res.append((record.id, name))
+ return res
+
hr_holidays_status()
class hr_holidays(osv.osv):
@@ -113,6 +125,13 @@ class hr_holidays(osv.osv):
result[hol.id] = hol.number_of_days_temp
return result
+ def _check_date(self, cr, uid, ids):
+ for holiday in self.browse(cr, uid, ids):
+ holiday_ids = self.search(cr, uid, [('date_from', '<=', holiday.date_to), ('date_to', '>=', holiday.date_from), ('employee_id', '=', holiday.employee_id.id), ('id', '<>', holiday.id)])
+ if holiday_ids:
+ return False
+ return True
+
_columns = {
'name': fields.char('Description', size=64),
'state': fields.selection([('draft', 'To Submit'), ('cancel', 'Cancelled'),('confirm', 'To Approve'), ('refuse', 'Refused'), ('validate1', 'Second Approval'), ('validate', 'Approved')],
@@ -146,6 +165,10 @@ class hr_holidays(osv.osv):
'user_id': lambda obj, cr, uid, context: uid,
'holiday_type': 'employee'
}
+ _constraints = [
+ (_check_date, 'You can not have 2 leaves that overlaps on same day!', ['date_from','date_to']),
+ ]
+
_sql_constraints = [
('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "The employee or employee category of this request is missing."),
('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be anterior to the end date."),
@@ -252,6 +275,13 @@ class hr_holidays(osv.osv):
result['value']['number_of_days_temp'] = 0
return result
+
+ def write(self, cr, uid, ids, vals, context=None):
+ check_fnct = self.pool.get('hr.holidays.status').check_access_rights
+ for holiday in self.browse(cr, uid, ids, context=context):
+ if holiday.state in ('validate','validate1') and not check_fnct(cr, uid, 'write', raise_exception=False):
+ raise osv.except_osv(_('Warning!'),_('You cannot modify a leave request that has been approved. Contact a human resource manager.'))
+ return super(hr_holidays, self).write(cr, uid, ids, vals, context=context)
def set_to_draft(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {
diff --git a/addons/hr_holidays/hr_holidays_demo.xml b/addons/hr_holidays/hr_holidays_demo.xml
index 4c9d18683c7..accf8c26935 100644
--- a/addons/hr_holidays/hr_holidays_demo.xml
+++ b/addons/hr_holidays/hr_holidays_demo.xml
@@ -34,8 +34,8 @@
Summer Vacation
-
-
+
+
add
draft
7
@@ -45,8 +45,8 @@
International Tour
-
-
+
+
add
7
diff --git a/addons/hr_holidays/report/holidays_summary_report.py b/addons/hr_holidays/report/holidays_summary_report.py
index 8a782075853..cadd3a22ce6 100644
--- a/addons/hr_holidays/report/holidays_summary_report.py
+++ b/addons/hr_holidays/report/holidays_summary_report.py
@@ -31,6 +31,7 @@ import time
from report import report_sxw
from tools import ustr
from tools.translate import _
+from tools import to_xml
def lengthmonth(year, month):
if month == 2 and ((year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))):
@@ -240,7 +241,7 @@ class report_custom(report_rml):
%s
%s
- ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,uid).company_id.name)
+ ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),to_xml(pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,uid).company_id.name))
# Computing the xml
xml='''
diff --git a/addons/hr_payroll/hr_payroll.py b/addons/hr_payroll/hr_payroll.py
index 53c84c65ee6..c0e4bc39321 100644
--- a/addons/hr_payroll/hr_payroll.py
+++ b/addons/hr_payroll/hr_payroll.py
@@ -326,6 +326,7 @@ class hr_payslip(osv.osv):
return self.write(cr, uid, ids, {'paid': True, 'state': 'done'}, context=context)
def hr_verify_sheet(self, cr, uid, ids, context=None):
+ self.compute_sheet(cr, uid, ids, context)
return self.write(cr, uid, ids, {'state': 'verify'}, context=context)
def refund_sheet(self, cr, uid, ids, context=None):
@@ -358,6 +359,12 @@ class hr_payslip(osv.osv):
def check_done(self, cr, uid, ids, context=None):
return True
+ def unlink(self, cr, uid, ids, context=None):
+ for payslip in self.browse(cr, uid, ids, context=context):
+ if payslip.state not in ['draft','cancel']:
+ raise osv.except_osv(_('Warning!'),_('You cannot delete a payslip which is not draft or cancelled!'))
+ return super(hr_payslip, self).unlink(cr, uid, ids, context)
+
#TODO move this function into hr_contract module, on hr.employee object
def get_contract(self, cr, uid, employee, date_from, date_to, context=None):
"""
diff --git a/addons/hr_payroll/hr_payroll_demo.xml b/addons/hr_payroll/hr_payroll_demo.xml
index 836814860c1..a22809b00e3 100644
--- a/addons/hr_payroll/hr_payroll_demo.xml
+++ b/addons/hr_payroll/hr_payroll_demo.xml
@@ -75,7 +75,7 @@
fix
- worked_days.WORK100.number_of_days
+ worked_days.WORK100 and worked_days.WORK100.number_of_days
MA
@@ -89,7 +89,7 @@
Get 1% of sales
- result = (inputs.SALEURO.amount + inputs.SALASIA.amount) * 0.01
+ result = ((inputs.SALEURO and inputs.SALEURO.amount) + (inputs.SALASIA and inputs.SALASIA.amount)) * 0.01
diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml
index 03561da569e..0dd58937306 100644
--- a/addons/hr_recruitment/hr_recruitment_view.xml
+++ b/addons/hr_recruitment/hr_recruitment_view.xml
@@ -106,7 +106,7 @@
-
@@ -333,7 +333,7 @@
hr.job
-
+
diff --git a/addons/hr_recruitment/static/src/js/hr_recruitment.js b/addons/hr_recruitment/static/src/js/hr_recruitment.js
index 9a0a6e4c894..d11326ac9de 100644
--- a/addons/hr_recruitment/static/src/js/hr_recruitment.js
+++ b/addons/hr_recruitment/static/src/js/hr_recruitment.js
@@ -16,7 +16,7 @@ openerp.hr_recruitment = function(openerp) {
// Find their matching names
var dataset = new openerp.web.DataSetSearch(self, 'hr.applicant_category', self.session.context, [['id', 'in', _.uniq(categ_ids)]]);
- dataset.read_slice(['id', 'name']).then(function(result) {
+ dataset.read_slice(['id', 'name']).done(function(result) {
_.each(result, function(v, k) {
// Set the proper value in the DOM and display the element
self.$el.find('span[data-categ_id=' + v.id + ']').text(v.name);
diff --git a/addons/hr_timesheet/report/user_timesheet.py b/addons/hr_timesheet/report/user_timesheet.py
index 7699f94f41b..8d2a7d35678 100644
--- a/addons/hr_timesheet/report/user_timesheet.py
+++ b/addons/hr_timesheet/report/user_timesheet.py
@@ -28,6 +28,7 @@ import time
import pooler
from report import report_sxw
from tools import ustr
+from tools import to_xml
def lengthmonth(year, month):
if month == 2 and ((year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))):
@@ -94,7 +95,7 @@ class report_custom(report_rml):
%s
%s
- ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,user_id).company_id.name)
+ ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),to_xml(pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,user_id).company_id.name))
account_xml = []
for account, telems in accounts.iteritems():
diff --git a/addons/hr_timesheet/wizard/hr_timesheet_print_employee.py b/addons/hr_timesheet/wizard/hr_timesheet_print_employee.py
index 75bb089bbc7..96ce23fb855 100644
--- a/addons/hr_timesheet/wizard/hr_timesheet_print_employee.py
+++ b/addons/hr_timesheet/wizard/hr_timesheet_print_employee.py
@@ -27,8 +27,9 @@ class analytical_timesheet_employee(osv.osv_memory):
_name = 'hr.analytical.timesheet.employee'
_description = 'Print Employee Timesheet & Print My Timesheet'
_columns = {
- 'month': fields.selection([(x, datetime.date(2000, x, 1).strftime('%B')) for x in range(1, 13)],
- 'Month', required=True),
+ 'month': fields.selection([(1,'January'), (2,'February'), (3,'March'), (4,'April'),
+ (5,'May'), (6,'June'), (7,'July'), (8,'August'), (9,'September'),
+ (10,'October'), (11,'November'), (12,'December')], 'Month', required=True),
'year': fields.integer('Year', required=True),
'employee_id': fields.many2one('hr.employee', 'Employee', required=True)
diff --git a/addons/hr_timesheet/wizard/hr_timesheet_print_users.py b/addons/hr_timesheet/wizard/hr_timesheet_print_users.py
index 865ccacb393..2a91bb607a4 100644
--- a/addons/hr_timesheet/wizard/hr_timesheet_print_users.py
+++ b/addons/hr_timesheet/wizard/hr_timesheet_print_users.py
@@ -27,8 +27,9 @@ class analytical_timesheet_employees(osv.osv_memory):
_name = 'hr.analytical.timesheet.users'
_description = 'Print Employees Timesheet'
_columns = {
- 'month': fields.selection([(x, datetime.date(2000, x, 1).strftime('%B')) for x in range(1, 13)],
- 'Month', required=True),
+ 'month': fields.selection([(1,'January'), (2,'February'), (3,'March'), (4,'April'),
+ (5,'May'), (6,'June'), (7,'July'), (8,'August'), (9,'September'),
+ (10,'October'), (11,'November'), (12,'December')], 'Month', required=True),
'year': fields.integer('Year', required=True),
'employee_ids': fields.many2many('hr.employee', 'timesheet_employee_rel', 'timesheet_id', 'employee_id', 'employees', required=True)
}
diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice_demo.xml b/addons/hr_timesheet_invoice/hr_timesheet_invoice_demo.xml
index cc531fc8f10..0ebe16ad49f 100644
--- a/addons/hr_timesheet_invoice/hr_timesheet_invoice_demo.xml
+++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice_demo.xml
@@ -7,7 +7,7 @@
50.0
- Gratis
+ Free of charge
Offered developments
100.0
diff --git a/addons/hr_timesheet_sheet/static/src/js/timesheet.js b/addons/hr_timesheet_sheet/static/src/js/timesheet.js
index e64c4d602e8..efff48d93b8 100644
--- a/addons/hr_timesheet_sheet/static/src/js/timesheet.js
+++ b/addons/hr_timesheet_sheet/static/src/js/timesheet.js
@@ -46,7 +46,7 @@ openerp.hr_timesheet_sheet = function(instance) {
var commands = this.field_manager.get_field_value("timesheet_ids");
this.res_o2m_drop.add(new instance.web.Model(this.view.model).call("resolve_2many_commands", ["timesheet_ids", commands, [],
new instance.web.CompoundContext()]))
- .then(function(result) {
+ .done(function(result) {
self.querying = true;
self.set({sheets: result});
self.querying = false;
@@ -57,7 +57,7 @@ openerp.hr_timesheet_sheet = function(instance) {
if (self.querying)
return;
self.updating = true;
- self.field_manager.set_values({timesheet_ids: self.get("sheets")}).then(function() {
+ self.field_manager.set_values({timesheet_ids: self.get("sheets")}).done(function() {
self.updating = false;
});
},
@@ -85,7 +85,7 @@ openerp.hr_timesheet_sheet = function(instance) {
var default_get;
return this.render_drop.add(new instance.web.Model("hr.analytic.timesheet").call("default_get", [
['account_id','general_account_id', 'journal_id','date','name','user_id','product_id','product_uom_id','to_invoice','amount','unit_amount'],
- new instance.web.CompoundContext({'user_id': self.get('user_id')})]).pipe(function(result) {
+ new instance.web.CompoundContext({'user_id': self.get('user_id')})]).then(function(result) {
default_get = result;
// calculating dates
dates = [];
@@ -108,9 +108,9 @@ openerp.hr_timesheet_sheet = function(instance) {
var account_ids = _.map(_.keys(accounts), function(el) { return el === "false" ? false : Number(el) });
return new instance.web.Model("hr.analytic.timesheet").call("multi_on_change_account_id", [[], account_ids,
- new instance.web.CompoundContext({'user_id': self.get('user_id')})]).pipe(function(accounts_defaults) {
+ new instance.web.CompoundContext({'user_id': self.get('user_id')})]).then(function(accounts_defaults) {
accounts = _(accounts).chain().map(function(lines, account_id) {
- account_defaults = _.extend({}, default_get, accounts_defaults[account_id]);
+ account_defaults = _.extend({}, default_get, (accounts_defaults[account_id] || {}).value || {});
// group by days
account_id = account_id === "false" ? false : Number(account_id);
var index = _.groupBy(lines, "date");
@@ -136,7 +136,7 @@ openerp.hr_timesheet_sheet = function(instance) {
// we need the name_get of the analytic accounts
return new instance.web.Model("account.analytic.account").call("name_get", [_.pluck(accounts, "account"),
- new instance.web.CompoundContext()]).pipe(function(result) {
+ new instance.web.CompoundContext()]).then(function(result) {
account_names = {};
_.each(result, function(el) {
account_names[el[0]] = el[1];
@@ -146,7 +146,7 @@ openerp.hr_timesheet_sheet = function(instance) {
});
});;
});
- })).pipe(function(result) {
+ })).then(function(result) {
// we put all the gathered data in self, then we render
self.dates = dates;
self.accounts = accounts;
@@ -222,7 +222,7 @@ openerp.hr_timesheet_sheet = function(instance) {
return;
}
var ops = self.generate_o2m_value();
- new instance.web.Model("hr.analytic.timesheet").call("on_change_account_id", [[], id]).pipe(function(res) {
+ new instance.web.Model("hr.analytic.timesheet").call("on_change_account_id", [[], id]).then(function(res) {
var def = _.extend({}, self.default_get, res.value, {
name: self.description_line,
unit_amount: 0,
diff --git a/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py b/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py
index d15b300597e..53948945e04 100644
--- a/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py
+++ b/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py
@@ -65,6 +65,8 @@ class partner_vat(osv.osv_memory):
partners = []
partner_ids = obj_partner.search(cr, uid, [('vat_subjected', '!=', False), ('vat','ilike','BE%')], context=context)
+ if not partner_ids:
+ raise osv.except_osv(_('Error'),_('No belgian contact with a VAT number in your database.'))
cr.execute("""SELECT sub1.partner_id, sub1.name, sub1.vat, sub1.turnover, sub2.vat_amount
FROM (SELECT l.partner_id, p.name, p.vat, SUM(CASE WHEN c.code ='49' THEN -l.tax_amount ELSE l.tax_amount END) as turnover
FROM account_move_line l
@@ -190,7 +192,7 @@ class partner_vat_list(osv.osv_memory):
addr = obj_partner.address_get(cr, uid, [obj_cmpny.partner_id.id], ['invoice'])
if addr.get('invoice',False):
ads = obj_partner.browse(cr, uid, [addr['invoice']], context=context)[0]
- phone = ads.phone.replace(' ','') or ''
+ phone = ads.phone and ads.phone.replace(' ','') or ''
email = ads.email or ''
name = ads.name or ''
city = ads.city or ''
@@ -259,6 +261,8 @@ class partner_vat_list(osv.osv_memory):
# Turnover and Farmer tags are not included
client_datas = self._get_datas(cr, uid, ids, context=context)
+ if not client_datas:
+ raise osv.except_osv(_('Data Insufficient!'),_('No data available for the client.'))
data_client_info = ''
for amount_data in client_datas:
data_client_info += """
@@ -309,6 +313,8 @@ class partner_vat_list(osv.osv_memory):
datas['year'] = context['year']
datas['limit_amount'] = context['limit_amount']
datas['client_datas'] = self._get_datas(cr, uid, ids, context=context)
+ if not datas['client_datas']:
+ raise osv.except_osv(_('Error!'),_('No record to print.'))
return {
'type': 'ir.actions.report.xml',
'report_name': 'partner.vat.listing.print',
diff --git a/addons/l10n_ch/bank.py b/addons/l10n_ch/bank.py
index 79dcb1a6834..4e4c0e0aa8c 100644
--- a/addons/l10n_ch/bank.py
+++ b/addons/l10n_ch/bank.py
@@ -52,21 +52,6 @@ class ResPartnerBank(osv.osv):
'my_bank': fields.boolean('Use my account to print BVR ?', help="Check to print BVR invoices"),
}
- def name_get(self, cursor, uid, ids, context=None):
- if not len(ids):
- return []
- bank_type_obj = self.pool.get('res.partner.bank.type')
-
- type_ids = bank_type_obj.search(cursor, uid, [])
- bank_type_names = {}
- for bank_type in bank_type_obj.browse(cursor, uid, type_ids,
- context=context):
- bank_type_names[bank_type.code] = bank_type.name
- res = []
- for r in self.read(cursor, uid, ids, ['name','state'], context):
- res.append((r['id'], r['name']+' : '+bank_type_names.get(r['state'], '')))
- return res
-
def _prepare_name(self, bank):
"Hook to get bank number of bank account"
res = u''
diff --git a/addons/l10n_fr/fr_tax.xml b/addons/l10n_fr/fr_tax.xml
index fc5d4608f45..36c5feb47b6 100644
--- a/addons/l10n_fr/fr_tax.xml
+++ b/addons/l10n_fr/fr_tax.xml
@@ -131,8 +131,8 @@
-
-
+
+
diff --git a/addons/l10n_fr/plan_comptable_general.xml b/addons/l10n_fr/plan_comptable_general.xml
index 525dabfcc8a..268f4bd6336 100644
--- a/addons/l10n_fr/plan_comptable_general.xml
+++ b/addons/l10n_fr/plan_comptable_general.xml
@@ -3903,7 +3903,7 @@
Associés - Comptes courants
455
view
-
+
Pour frais avancés personnellement
@@ -3912,8 +3912,8 @@
Principal
4551
- receivable
-
+ payable
+
@@ -3921,8 +3921,8 @@
Intérêts courus
4558
- receivable
-
+ payable
+
diff --git a/addons/l10n_it/data/account.tax.code.template.csv b/addons/l10n_it/data/account.tax.code.template.csv
index 290396052f4..d6d6667f43b 100644
--- a/addons/l10n_it/data/account.tax.code.template.csv
+++ b/addons/l10n_it/data/account.tax.code.template.csv
@@ -60,6 +60,3 @@ IVC21det40,template_ivacode_pagata_21det40,IVA a credito 21% detraibile 40%,temp
IVC21Idet40,template_impcode_pagata_21det40,IVA a credito 21% detraibile 40% (imponibile),template_impcode_pagata
IVC21det50,template_ivacode_pagata_21det50,IVA a credito 21% detraibile 50%,template_ivacode_pagata
IVC21Idet50,template_impcode_pagata_21det50,IVA a credito 21% detraibile 50% (imponibile),template_impcode_pagata
-Rit,template_ra,Ritenute d'acconto,vat_code_chart_root
-RitD20,template_ritcode_20,Ritenute a debito 20%,template_ra
-RitD20I,template_ritimpcode_20,Ritenute a debito 20% (imponibile),template_ra
diff --git a/addons/l10n_it/data/account.tax.template.csv b/addons/l10n_it/data/account.tax.template.csv
index 36c4f99db79..641f495dd72 100644
--- a/addons/l10n_it/data/account.tax.template.csv
+++ b/addons/l10n_it/data/account.tax.template.csv
@@ -62,4 +62,3 @@ id,description,chart_template_id:id,name,sequence,amount,parent_id:id,child_depe
21I5,21I5,l10n_it_chart_template_generic,IVA al 21% detraibile al 50%,,0.21,,True,percent,,,purchase,template_impcode_pagata_21det50,,template_impcode_pagata_21det50,,,,False,-1,-1
21I5b,21I5b,l10n_it_chart_template_generic,IVA al 21% detraibile al 50% (I),1,0.5,21I5,False,percent,,,purchase,,,,,,,False,,
21I5a,21I5a,l10n_it_chart_template_generic,IVA al 21% detraibile al 50% (D),2,0,21I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_21det50,,template_ivacode_pagata_21det50,,,False,,
-rit-20,rit-20,l10n_it_chart_template_generic,Ritenuta d'acconto al 20% (debito),,-0.2,,False,percent,2602,2602,purchase,template_ritimpcode_20,template_ritcode_20,template_ritimpcode_20,template_ritcode_20,-1,1,False,1,-1
diff --git a/addons/l10n_nl/account_chart_netherlands.xml b/addons/l10n_nl/account_chart_netherlands.xml
index 96100d9a442..f736c6f4895 100644
--- a/addons/l10n_nl/account_chart_netherlands.xml
+++ b/addons/l10n_nl/account_chart_netherlands.xml
@@ -1424,7 +1424,7 @@
-
+
Btw af te dragen laag
1601
other
@@ -1432,7 +1432,7 @@
-
+
Btw af te dragen hoog
1602
other
@@ -1448,7 +1448,7 @@
-
+
Btw te vorderen laag
1611
other
@@ -1456,7 +1456,7 @@
-
+
Btw te vorderen hoog
1612
other
@@ -3909,7 +3909,7 @@
1a
- Leveringen/diensten belast met 19% (BTW)
+ Leveringen/diensten belast met 21% (BTW)
1b
@@ -4021,7 +4021,7 @@
1a
- Leveringen/diensten belast met 19% (omzet)
+ Leveringen/diensten belast met 21% (omzet)
1b
@@ -4149,22 +4149,22 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
6% BTW
percent
-
-
+
+
sale
-
+
Verkopen/omzet hoog
- 19% BTW
-
+ 21% BTW
+
percent
-
-
+
+
@@ -4176,10 +4176,10 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
Verkopen/omzet overig
variabel BTW
-
+
percent
-
-
+
+
@@ -4194,20 +4194,20 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
6% BTW
percent
-
-
+
+
purchase
-
+
BTW te vorderen hoog (inkopen)
- 19% BTW
-
+ 21% BTW
+
percent
-
-
+
+
purchase
@@ -4216,10 +4216,10 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
BTW te vorderen overig (inkopen)
variabel BTW
-
+
percent
-
-
+
+
purchase
@@ -4240,8 +4240,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
BTW af te dragen verlegd (inkopen)
- 19% BTW verlegd
-
+ 21% BTW verlegd
+
percent
@@ -4277,8 +4277,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
BTW te vorderen verlegd (inkopen)
- 19% BTW verlegd
-
+ 21% BTW verlegd
+
percent
@@ -4330,8 +4330,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
@@ -4342,43 +4342,43 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
-
+
Inkopen import binnen EU hoog
- 19% BTW import binnen EU
-
+ 21% BTW import binnen EU
+
percent
purchase
-
+
Inkopen import binnen EU hoog(1)
percent
-
-
-
+
+
+
purchase
-
+
Inkopen import binnen EU hoog(2)
percent
-
-
-
+
+
+
purchase
@@ -4400,8 +4400,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
@@ -4412,8 +4412,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
@@ -4463,8 +4463,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
@@ -4475,8 +4475,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
@@ -4485,7 +4485,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
Inkopen import buiten EU hoog
BTW import buiten EU
-
+
percent
@@ -4498,8 +4498,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
@@ -4510,8 +4510,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
@@ -4520,7 +4520,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
Inkopen import buiten EU overig
BTW import buiten EU
-
+
percent
@@ -4533,8 +4533,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
@@ -4545,8 +4545,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
percent
-
-
+
+
purchase
diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py
index aea7ee62cbc..b34993ffd32 100644
--- a/addons/mail/__init__.py
+++ b/addons/mail/__init__.py
@@ -22,12 +22,12 @@
import mail_message_subtype
import mail_alias
import mail_followers
+import mail_vote
+import mail_favorite
import mail_message
import mail_mail
import mail_thread
import mail_group
-import mail_vote
-import mail_favorite
import res_partner
import res_users
import report
diff --git a/addons/mail/data/mail_demo.xml b/addons/mail/data/mail_demo.xml
index bda93fc485d..f189b10650c 100644
--- a/addons/mail/data/mail_demo.xml
+++ b/addons/mail/data/mail_demo.xml
@@ -2,6 +2,12 @@
+
+
+ none
+
+
+
mail.group
@@ -10,7 +16,6 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
comment
-
-
-
+
-
+
+
+ Hello Demo User! I was wondering whether you had some issues with our secret task about putting cats everywhere in OpenERP.
+ comment
+
+
+
+
+
+ No specific issues, I think everything is clear.
+
+ comment
+
+
+
+
+
+ Ow, just to be sure... we were talking about lolcats, right ?
+
+ comment
+
+
+
+
+
+ Absolutely!
+
+ comment
+
+
+
+
+
+
+
+ Plan to install OpenERP
+ mail.message
+
+ <![CDATA[Email0 inquiry]]>
+ <div><font size="2">Hello,</font></div><div><font size="2"><br></font></div>
+ <div><font size="2">I am interested in your company's product and I plan to install OpenERP for my company and affordable price.</font></div><br/>
+ <div><font size="2">Can you please send me services catalogue?</font></div><br/>
+ Sophie
+
+ email
+
+
+
+
+ ir.attachment
+ bWlncmF0aW9uIHRlc3Q=
+ catalogue 2012.pdf
+ catalogue 2012.pdf
+
+
+ Re: Plan to install OpenERP
+
+ Dear Customer,<br/>
+ Thanks for showing interest in our products.<br/>
+ We have attached the catalogue,<br/>
+ We would like to know your interests, so let us know when we can call you for more details.<br/>
+ Regards
+
+ comment
+
+
+
+
+
+
+
+
+
+ ir.attachment
+ bWlncmF0aW9uIHRlc3Q=
+ migration.doc
+ migration.doc
+
+
+ ir.attachment
+
+ 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
+ CAEAIAQAgP/Z
+
+ activity graph 2012.jpg
+ activity graph 2012
+
+
+ mail.group
+
+ Hi,<br/>
+ The beta OpenERP 7 is scheduled for November 12.<br/>
+ You will find attached the document for migration from version 6 to 7, and the activity graph for the current year. Good reading.<br/>
+ Sincerely
+
+ comment
+
+
+
+
+
+
+ mail.group
+
+ Thank you,<br/>
+ Could you prepare and send us also the document for version 7.1 which will come soon?<br/>
+ Sincerely
+
+ comment
+
+
+
+
+
+
+
+
+ mail.group
+ I changed the infrastructure of networks, if there are still changes to be made please do not hesitate to contact me.
+ comment
+
+
+
+
+
+ mail.group
+ Thank you, the networks is perfect now ! Could you add a IP phone for Jhon ?
+
+ comment
+
+
+
+
+
+ mail.group
+ It's right, his internal phone number is 0093
+
+ comment
+
+
+
+
+
diff --git a/addons/mail/data/mail_group_data.xml b/addons/mail/data/mail_group_data.xml
index 11d0ee7d04b..2daa69b283b 100644
--- a/addons/mail/data/mail_group_data.xml
+++ b/addons/mail/data/mail_group_data.xml
@@ -9,7 +9,7 @@
Whole Company
- Discussion about best sales practices and deals.
+ General announces for all employees.
diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py
index e8cf2648f29..6a8b24f85a8 100644
--- a/addons/mail/mail_followers.py
+++ b/addons/mail/mail_followers.py
@@ -61,10 +61,10 @@ class mail_notification(osv.Model):
_columns = {
'partner_id': fields.many2one('res.partner', string='Contact',
- ondelete='cascade', required=True),
- 'read': fields.boolean('Read'),
+ ondelete='cascade', required=True, select=1),
+ 'read': fields.boolean('Read', select=1),
'message_id': fields.many2one('mail.message', string='Message',
- ondelete='cascade', required=True),
+ ondelete='cascade', required=True, select=1),
}
_defaults = {
@@ -102,7 +102,7 @@ class mail_notification(osv.Model):
# some messages do not have notifications: find which one, create notification, update read status
exist_notification = dict.fromkeys(msg_ids, False)
for notification in self.browse(cr, uid, notif_ids, context=context):
- exist_notification[notification.message_id] = True
+ exist_notification[notification.message_id.id] = True
for msg_id in exist_notification.keys():
self.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context)
return self.write(cr, uid, notif_ids, {'read': read}, context=context)
diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml
index 2028e8d1b18..214f6edf571 100644
--- a/addons/mail/mail_group_view.xml
+++ b/addons/mail/mail_group_view.xml
@@ -32,17 +32,18 @@
-
+
+
+
+
+
+ Unfollow
+ Join Group
+
-
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index bd0ade31b68..c72af5eadaf 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -20,6 +20,7 @@
##############################################################################
import logging
+import pdb
import tools
from email.header import decode_header
@@ -50,8 +51,9 @@ class mail_message(osv.Model):
_description = 'Message'
_inherit = ['ir.needaction_mixin']
_order = 'id desc'
+ _rec_name = 'record_name'
- _message_read_limit = 10
+ _message_read_limit = 30
_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
@@ -120,21 +122,26 @@ class mail_message(osv.Model):
"message, comment for other messages such as user replies"),
'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',
+ 'author_id': fields.many2one('res.partner', 'Author', select=1,
+ ondelete='set null',
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'),
+ 'message_id', 'partner_id', 'Notified partners',
+ help='Partners that have a notification pushing this message in their mailboxes'),
'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."),
+ '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='char',
store=True, string='Message Record Name',
help="Name get of the related document."),
- 'notification_ids': fields.one2many('mail.notification', 'message_id', 'Notifications'),
+ 'notification_ids': fields.one2many('mail.notification', 'message_id',
+ string='Notifications',
+ help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'),
'subject': fields.char('Subject'),
'date': fields.datetime('Date'),
'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1),
@@ -142,7 +149,8 @@ class mail_message(osv.Model):
'to_read': fields.function(_get_to_read, fnct_search=_search_to_read,
type='boolean', string='To read',
help='Functional field to search for messages the current user has to read'),
- 'subtype_id': fields.many2one('mail.message.subtype', 'Subtype'),
+ 'subtype_id': fields.many2one('mail.message.subtype', 'Subtype',
+ ondelete='set null', select=1,),
'vote_user_ids': fields.many2many('res.users', 'mail_vote',
'message_id', 'user_id', string='Votes',
help='Users that voted for this message'),
@@ -200,67 +208,99 @@ class mail_message(osv.Model):
# Message loading for web interface
#------------------------------------------------------
- def _message_get_dict(self, cr, uid, message, context=None):
- """ Return a dict representation of the message. This representation is
- used in the JS client code, to display the messages.
+ def _message_read_dict_postprocess(self, cr, uid, messages, message_tree, context=None):
+ """ Post-processing on values given by message_read. This method will
+ handle partners in batch to avoid doing numerous queries.
- :param dict message: read result of a mail.message
+ :param list messages: list of message, as get_dict result
+ :param dict message_tree: {[msg.id]: msg browse record}
"""
- # 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'])
+ res_partner_obj = self.pool.get('res.partner')
+ ir_attachment_obj = self.pool.get('ir.attachment')
+ pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
- has_voted = False
- if uid in message.get('vote_user_ids'):
- has_voted = True
+ # 1. Aggregate partners (author_id and partner_ids) and attachments
+ partner_ids = set()
+ attachment_ids = set()
+ for key, message in message_tree.iteritems():
+ if message.author_id:
+ partner_ids |= set([message.author_id.id])
+ if message.partner_ids:
+ partner_ids |= set([partner.id for partner in message.partner_ids])
+ if message.attachment_ids:
+ attachment_ids |= set([attachment.id for attachment in message.attachment_ids])
- is_favorite = False
- if uid in message.get('favorite_user_ids'):
- is_favorite = True
+ # Filter author_ids uid can see
+ # partner_ids = self.pool.get('res.partner').search(cr, uid, [('id', 'in', partner_ids)], context=context)
+ partners = res_partner_obj.name_get(cr, uid, list(partner_ids), context=context)
+ partner_tree = dict((partner[0], partner) for partner in partners)
- is_private = True
- if message.get('model') and message.get('res_id'):
- is_private = False
+ # 2. Attachments
+ attachments = ir_attachment_obj.read(cr, uid, list(attachment_ids), ['id', 'datas_fname'], context=context)
+ attachments_tree = dict((attachment['id'], {'id': attachment['id'], 'filename': attachment['datas_fname']}) for attachment in attachments)
- 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):
+ # 3. Update message dictionaries
+ for message_dict in messages:
+ message_id = message_dict.get('id')
+ message = message_tree[message_id]
+ if message.author_id:
+ author = partner_tree[message.author_id.id]
+ else:
+ author = (0, message.email_from)
partner_ids = []
+ for partner in message.partner_ids:
+ if partner.id in partner_tree:
+ partner_ids.append(partner_tree[partner.id])
+ attachment_ids = []
+ for attachment in message.attachment_ids:
+ if attachment.id in attachments_tree:
+ attachment_ids.append(attachments_tree[attachment.id])
+ message_dict.update({
+ 'is_author': pid == author[0],
+ 'author_id': author,
+ 'partner_ids': partner_ids,
+ 'attachment_ids': attachment_ids,
+ })
+ return True
- return {
- 'id': message['id'],
- 'type': message['type'],
- 'attachment_ids': attachment_ids,
- 'body': message['body'],
- 'model': message['model'],
- 'res_id': message['res_id'],
- 'record_name': message['record_name'],
- 'subject': message['subject'],
- 'date': message['date'],
- 'author_id': author_id,
- 'is_author': is_author,
- 'partner_ids': partner_ids,
- 'parent_id': False,
- 'vote_nb': len(message['vote_user_ids']),
- 'has_voted': has_voted,
- 'is_private': is_private,
- 'is_favorite': is_favorite,
- 'to_read': message['to_read'],
- }
+ def _message_read_dict(self, cr, uid, message, parent_id=False, context=None):
+ """ Return a dict representation of the message. This representation is
+ used in the JS client code, to display the messages. Partners and
+ attachments related stuff will be done in post-processing in batch.
- 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):
+ :param dict message: mail.message browse record
+ """
+ # private message: no model, no res_id
+ is_private = False
+ if not message.model or not message.res_id:
+ is_private = True
+ # votes and favorites: res.users ids, no prefetching should be done
+ vote_nb = len(message.vote_user_ids)
+ has_voted = uid in [user.id for user in message.vote_user_ids]
+ is_favorite = uid in [user.id for user in message.favorite_user_ids]
+
+ return {'id': message.id,
+ 'type': message.type,
+ 'body': message.body,
+ 'model': message.model,
+ 'res_id': message.res_id,
+ 'record_name': message.record_name,
+ 'subject': message.subject,
+ 'date': message.date,
+ 'to_read': message.to_read,
+ 'parent_id': parent_id,
+ 'is_private': is_private,
+ 'author_id': False,
+ 'is_author': False,
+ 'partner_ids': [],
+ 'vote_nb': vote_nb,
+ 'has_voted': has_voted,
+ 'is_favorite': is_favorite,
+ 'attachment_ids': [],
+ }
+
+ def _message_read_add_expandables(self, cr, uid, messages, message_tree, parent_tree,
+ message_unload_ids=[], thread_level=0, domain=[], parent_id=False, context=None):
""" Create expandables for message_read, to load new messages.
1. get the expandable for new threads
if display is flat (thread_level == 0):
@@ -275,96 +315,82 @@ class mail_message(osv.Model):
for each hole in the child list based on message displayed,
create an expandable
- :param list message_list:list of message structure for the Chatter
+ :param list messages: 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
+ :param dict message_tree: dict [id]: browse record of this message
+ :param dict parent_tree: dict [parent_id]: [child_ids]
+ :param list message_unload_ids: list of message_ids we do not want
+ to load
:return bool: True
"""
- def _get_expandable(domain, message_nb, parent_id, id, model):
+ def _get_expandable(domain, message_nb, parent_id, max_limit):
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,
+ 'max_limit': max_limit,
}
- # all_not_loaded_ids = []
- id_list = sorted(read_messages.keys())
- if not id_list:
- return message_list
+ if not messages:
+ return True
+ message_ids = sorted(message_tree.keys())
# 1. get the expandable for new threads
if thread_level == 0:
- exp_domain = domain + [('id', '<', min(message_loaded_ids + id_list))]
+ exp_domain = domain + [('id', '<', min(message_unload_ids + message_ids))]
else:
- exp_domain = domain + ['!', ('id', 'child_of', message_loaded_ids + id_list)]
+ exp_domain = domain + ['!', ('id', 'child_of', message_unload_ids + parent_tree.keys())]
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))
+ # inside a thread: prepend
+ if parent_id:
+ messages.insert(0, _get_expandable(exp_domain, -1, parent_id, True))
+ # new threads: append
+ else:
+ messages.append(_get_expandable(exp_domain, -1, parent_id, True))
# 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]
+ for message_id in message_ids:
+ message = message_tree[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'):
+ # generate only for thread header messages (TDE note: parent_id may be False is uid cannot see parent_id, seems ok)
+ if message.parent_id:
continue
- # TDE note: check search is correctly implemented in mail.message
- not_loaded_ids = self.search(cr, uid, [
- ('id', 'child_of', message['id']),
- ('id', 'not in', message_loaded_ids),
- ], context=context, limit=self._message_read_more_limit)
- if not not_loaded_ids:
+ # check there are message for expandable
+ child_ids = set([child.id for child in message.child_ids]) - set(message_unload_ids)
+ child_ids = sorted(list(child_ids), reverse=True)
+ if not child_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):
+ # make groups of unread messages
+ id_min, id_max, nb = max(child_ids), 0, 0
+ for child_id in child_ids:
+ if not child_id in message_ids:
nb += 1
- if id_min > not_loaded_id:
- id_min = not_loaded_id
- if id_max < not_loaded_id:
- id_max = not_loaded_id
+ if id_min > child_id:
+ id_min = child_id
+ if id_max < child_id:
+ id_max = child_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
+ messages.append(_get_expandable(exp_domain, nb, message_id, False))
+ id_min, id_max, nb = max(child_ids), 0, 0
else:
- id_min, id_max, nb = max(not_loaded_ids), 0, 0
+ id_min, id_max, nb = max(child_ids), 0, 0
if 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')))
-
- # message_loaded_ids = list(set(message_loaded_ids + read_messages.keys() + all_not_loaded_ids))
+ idx = [msg.get('id') for msg in messages].index(message_id) + 1
+ # messages.append(_get_expandable(exp_domain, nb, message_id, id_min))
+ messages.insert(idx, _get_expandable(exp_domain, nb, message_id, False))
return True
- def _get_parent(self, cr, uid, message, context=None):
- """ Tools method that tries to get the parent of a mail.message. If
- no parent, or if uid has no access right on the parent, False
- is returned.
-
- :param dict message: read result of a mail.message
- """
- if not message['parent_id']:
- return False
- parent_id = message['parent_id'][0]
- try:
- return self.read(cr, uid, parent_id, self._message_read_fields, context=context)
- except (orm.except_orm, osv.except_osv):
- return False
-
- def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None, thread_level=0, context=None, parent_id=False, limit=None):
+ 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.
@@ -388,46 +414,56 @@ class mail_message(osv.Model):
ancestors and expandables
:return list: list of message structure for the Chatter widget
"""
- # 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_tree = {}
message_list = []
+ parent_tree = {}
# 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):
- 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))
+ # fetch parent if threaded, sort messages
+ for message in self.browse(cr, uid, ids, context=context):
+ message_id = message.id
+ if message_id in message_tree:
+ continue
+ message_tree[message_id] = message
- # 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:
- 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))
+ # find parent_id
+ if thread_level == 0:
+ tree_parent_id = parent_id
+ else:
+ tree_parent_id = message_id
+ parent = message
+ while parent.parent_id and parent.parent_id.id != parent_id:
+ parent = parent.parent_id
+ tree_parent_id = parent.id
+ if not parent.id in message_tree:
+ message_tree[parent.id] = parent
+ # newest messages first
+ parent_tree.setdefault(tree_parent_id, [])
+ if tree_parent_id != message_id:
+ parent_tree[tree_parent_id].append(self._message_read_dict(cr, uid, message_tree[message_id], parent_id=tree_parent_id, context=context))
+
+ if thread_level:
+ for key, message_id_list in parent_tree.iteritems():
+ message_id_list.sort(key=lambda item: item['id'])
+ message_id_list.insert(0, self._message_read_dict(cr, uid, message_tree[key], context=context))
+
+ parent_list = parent_tree.items()
+ parent_list = sorted(parent_list, key=lambda item: max([msg.get('id') for msg in item[1]]) if item[1] else item[0], reverse=True)
+ message_list = [message for (key, msg_list) in parent_list for message in msg_list]
# get the child expandable messages for the tree
- message_list = sorted(message_list, key=lambda k: k['id'])
- 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)
-
+ self._message_read_dict_postprocess(cr, uid, message_list, message_tree, context=context)
+ self._message_read_add_expandables(cr, uid, message_list, message_tree, parent_tree,
+ thread_level=thread_level, message_unload_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context)
return message_list
# TDE Note: do we need this ?
@@ -461,7 +497,6 @@ class mail_message(osv.Model):
- 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)
@@ -592,11 +627,14 @@ class mail_message(osv.Model):
other_ids = other_ids - set(document_related_ids)
if not other_ids:
return
+
raise orm.except_orm(_('Access Denied'),
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
(self._description, operation))
def create(self, cr, uid, values, context=None):
+ if 'default_res_model' in context:
+ values['model']=context.get('default_res_model')
if not values.get('message_id') and values.get('res_id') and values.get('model'):
values['message_id'] = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values)
newid = super(mail_message, self).create(cr, uid, values, context)
diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml
index 93b35797fd5..7d2e9483773 100644
--- a/addons/mail/mail_message_view.xml
+++ b/addons/mail/mail_message_view.xml
@@ -59,21 +59,26 @@
+
+
-
+
+
@@ -84,7 +89,6 @@
form
tree,form
- {'search_default_to_read_message':True}
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index f3203336615..ee00b88dbd9 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -74,17 +74,17 @@ class mail_thread(osv.AbstractModel):
- message_unread: has uid unread message for the document
- message_summary: html snippet summarizing the Chatter for kanban views """
res = dict((id, dict(message_unread=False, message_summary='')) for id in ids)
+ user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
- # search for unread messages, by reading directly mail.notification, as SUPERUSER
- notif_obj = self.pool.get('mail.notification')
- notif_ids = notif_obj.search(cr, SUPERUSER_ID, [
- ('partner_id.user_ids', 'in', [uid]),
- ('message_id.res_id', 'in', ids),
- ('message_id.model', '=', self._name),
- ('read', '=', False)
- ], context=context)
- for notif in notif_obj.browse(cr, SUPERUSER_ID, notif_ids, context=context):
- res[notif.message_id.res_id]['message_unread'] = True
+ # search for unread messages, directly in SQL to improve performances
+ cr.execute(""" SELECT m.res_id FROM mail_message m
+ RIGHT JOIN mail_notification n
+ ON (n.message_id = m.id AND n.partner_id = %s AND n.read = False)
+ WHERE m.model = %s AND m.res_id in %s""",
+ (user_pid, self._name, tuple(ids),))
+ msg_ids = [result[0] for result in cr.fetchall()]
+ for msg_id in msg_ids:
+ res[msg_id]['message_unread'] = True
for thread in self.browse(cr, uid, ids, context=context):
cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or ''
diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml
index 07b24725b05..cdeade04d84 100644
--- a/addons/mail/mail_thread_view.xml
+++ b/addons/mail/mail_thread_view.xml
@@ -4,48 +4,126 @@
Inbox
mail.wall
-
+ mail.message
+ {
+ 'default_model': 'res.users',
+ 'default_res_id': uid
+ }
+
+
+
+ Good Job! Your inbox is empty.
+
+ Your inbox contains private messages or emails sent to you
+ as well as information related to documents or people you
+ follow.
+
+
To: me
mail.wall
-
+ mail.message
+ {
+ 'default_model': 'res.users',
+ 'default_res_id': uid,
+ 'search_default_message_unread': True
+ }
+
+
+
+ No private message.
+
+ This list contains messages sent to you.
+
+
- Favorites
+ Todo
mail.wall
-
+ mail.message
+ {
+ 'default_model': 'res.users',
+ 'default_res_id': uid,
+ 'search_default_message_unread': True
+ }
+
+
+
+ No todo!
+
+ When you process messages in your inbox, you can mark some
+ as todo . From this menu, you can process all your todo.
+
+
Archives
mail.wall
-
+ {
+ 'default_model': 'res.users',
+ 'default_res_id': uid,
+ 'search_default_message_read': True
+ }
+
+
+
+ No message found.
+
+
Sent
mail.wall
-
+ {
+ 'default_model': 'res.users',
+ 'default_res_id': uid
+ }
+
+
+
+ No message sent yet.
+
+ Click on the top-right icon to compose a message. This
+ message will be sent by email if it's an internal contact.
+
+
-
-
+
@@ -61,7 +139,7 @@
- Favorites
+ Todo
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index af3cc3ed359..18d19a848cc 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -1,7 +1,36 @@
+/* ------------ TOPBAR MAIL BUTTON --------------- */
+
+/* FIXME this css is not very pretty because it uses a
+ * 'button' element wich comes with a lot of inappropriate
+ * styling. Entypo is also a headache to center properly
+ * */
+
+.openerp .oe_topbar_item.oe_topbar_compose_full_email{
+ padding: 0px;
+ width: 32px;
+ height: 32px;
+}
+.openerp .oe_topbar_item.oe_topbar_compose_full_email button{
+ position: relative;
+ top: -3px; /* centering entypo ... urgh */
+ box-sizing: border-box;
+ border: none;
+ box-shadow: none;
+ color: white;
+ background: none;
+ text-shadow: 0px 1px 2px black;
+ width: 32px;
+ height: 32px;
+ padding: 0px;
+ margin: 0px
+ border-radius: 0px;
+}
/* ------------ MAIL WIDGET --------------- */
.openerp .oe_mail, .openerp .oe_mail *{
- box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
}
.openerp .oe_mail {
display: block;
@@ -42,9 +71,10 @@
}
.openerp .oe_mail .oe_msg .oe_msg_footer{
padding-left: 4px;
+ padding-top: 3px;
overflow: hidden;
- opacity:0.8;
- -webkit-transition: opacity 0.2s linear;
+ margin-bottom: 4px;
+ font-size: 11px;
}
.openerp .oe_mail .oe_msg .oe_msg_content{
display: block;
@@ -80,18 +110,24 @@
.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_content{
padding-top:2px;
}
+.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_footer{
+ margin-bottom: 0px;
+}
+
/* b) Votes (likes) */
+
.openerp .oe_mail .oe_mail_vote_count{
display: inline;
position: relative;
- background: #7C7BAD;
- color: white;
+ background: white;
+ box-shadow: 0px 0px 0px 1px rgba(124, 123, 173, 0.36) inset;
+ color: #7c7bad;
text-shadow: none;
border-radius: 3px;
margin: 0px;
padding-left: 3px;
- padding-right: 18px;
- margin-right: 3px;
+ padding-right: 15px;
+ margin-right: 5px;
}
.openerp .oe_mail .oe_mail_vote_count .oe_e{
position: absolute;
@@ -102,12 +138,6 @@
/* c) Message action icons */
-.openerp .oe_mail .oe_msg.oe_msg_unread .oe_unread{
- display:none;
-}
-.openerp .oe_mail .oe_msg.oe_msg_read .oe_read{
- display:none;
-}
.openerp .oe_mail .oe_msg .oe_msg_icons{
float: right;
margin-top: 4px;
@@ -115,6 +145,9 @@
margin-left: 8px;
height: 24px;
-webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
}
.openerp .oe_mail .oe_msg .oe_msg_icons span{
float:right;
@@ -128,10 +161,16 @@
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;
+ -moz-transition: all 0.2s linear;
+ -o-transition: all 0.2s linear;
+ transition: all 0.2s linear;
}
.openerp .oe_mail .oe_msg:hover .oe_msg_icons a{
opacity: 1;
-webkit-transition: all 0.1s linear;
+ -moz-transition: all 0.1s linear;
+ -o-transition: all 0.1s linear;
+ transition: all 0.1s linear;
}
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star:hover a{
color: #FFF6C0;
@@ -155,7 +194,7 @@
}
.openerp .oe_mail .oe_msg .oe_msg_content textarea{
width: 100%;
- height: 32px;
+ height: 64px;
margin: 0px;
padding: 0px;
resize: vertical;
@@ -171,6 +210,150 @@
width: 100%;
}
+/* --------------------- ATTACHMENTS --------------------- */
+
+.openerp .oe_mail .oe_msg_attachment_list{
+ display: none;
+ margin-top: 12px;
+ margin-bottom: 12px;
+}
+.openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list{
+ display: block;
+}
+.openerp .oe_mail .oe_attachment{
+ display: inline-block;
+ width: 100px;
+ margin: 2px;
+ min-height: 80px;
+ position: relative;
+ border-radius: 3px;
+ text-align: center;
+ vertical-align: top;
+}
+.openerp .oe_mail .oe_attachment .oe_name{
+ display: inline-block;
+ max-width: 100%;
+ padding: 1px 3px;
+ margin-top: 50px;
+ margin-bottom: 5px;
+ background: rgba(124, 123, 173, 0.13);
+ overflow: hidden;
+ color: #4c4c4c;
+ text-shadow: none;
+ border-radius: 3px;
+}
+
+.openerp .oe_mail .oe_attachment.oe_preview{
+ background: url( data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAJ0lEQVQYV2MsLS39z4AGLCws0IUYGIeCwrVr12J45sSJE5ieGQIKAbuZKf/EMCs7AAAAAElFTkSuQmCC );
+}
+.openerp .oe_mail .oe_attachment .oe_progress_bar{
+ display: none;
+ position: absolute;
+ top: 18px;
+ left: 16px;
+ right: 16px;
+ height: 17px;
+ line-height: 13px;
+ padding: 0px;
+ background: #4BBD00;
+ color: white;
+ text-align: center;
+ border-radius: 3px;
+ border: solid 1px rgba(0,0,0,0.2);
+ box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.34);
+ -webkit-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
+ -moz-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
+ -o-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
+ animation: oe_mail_attach_loading_anim 0.75s infinite linear;
+}
+.openerp .oe_mail .oe_attachment.oe_uploading .oe_progress_bar{
+ display: block;
+}
+@-webkit-keyframes oe_mail_attach_loading_anim{
+ 0% { background: #4BBD00 }
+ 50% { background: #009123 }
+ 100% { background: #4BBD00 }
+}
+@-moz-keyframes oe_mail_attach_loading_anim{
+ 0% { background: #4BBD00 }
+ 50% { background: #009123 }
+ 100% { background: #4BBD00 }
+}
+@-o-keyframes oe_mail_attach_loading_anim{
+ 0% { background: #4BBD00 }
+ 50% { background: #009123 }
+ 100% { background: #4BBD00 }
+}
+@keyframes oe_mail_attach_loading_anim{
+ 0% { background: #4BBD00 }
+ 50% { background: #009123 }
+ 100% { background: #4BBD00 }
+}
+.openerp .oe_mail .oe_attachment.oe_preview .oe_name{
+ position: absolute;
+ bottom: 0px;
+ margin: 0px;
+ left: 0px;
+ right: 0px;
+ max-height: 64px;
+ background: rgba(0,0,0,0.8);
+ color: white;
+ border-top-left-radius: 0px;
+ border-top-right-radius: 0px;
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+}
+.openerp .oe_mail .oe_attachment.oe_preview:hover .oe_name{
+ opacity: 1;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+}
+.openerp .oe_mail .oe_attachment img{
+ position: absolute;
+ width: 48px;
+ height: 48px;
+ top: 0px;
+ left: 50%;
+ margin-left: -24px;
+}
+.openerp .oe_mail .oe_attachment.oe_preview img{
+ display: block;
+ position: relative;
+ margin:0px;
+ width: 100px;
+ height: 100px;
+ border-radius: 3px;
+ margin-left: -50px;
+}
+.openerp .oe_mail .oe_attachment .oe_delete{
+ display: none;
+}
+.openerp .oe_mail .oe_msg_composer .oe_attachment .oe_delete{
+ display: block;
+ position: absolute;
+ top: -7px;
+ right: 0px;
+ color: black;
+ text-shadow: 1px 0px white, -1px 0px white, 0px 1px white, 0px -1px white;
+ cursor: pointer;
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+}
+.openerp .oe_mail .oe_msg_composer .oe_attachment:hover .oe_delete{
+ opacity: 1;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+}
/* ---------------- MESSAGE QUICK COMPOSER --------------- */
.openerp .oe_mail .oe_msg_composer .oe_msg_footer{
@@ -178,37 +361,6 @@
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;
-}
-.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 .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;
}
@@ -269,6 +421,7 @@
.openerp .oe_mail .oe_msg_content.oe_msg_more_message{
text-align: right;
+ cursor: pointer;
}
.openerp .oe_mail .oe_msg_content.oe_msg_more_message .oe_separator{
height: 0;
@@ -280,7 +433,7 @@
}
.openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more {
background: white;
- margin-right: 280px;
+ margin-right: 210px;
padding-left: 8px;
padding-right: 8px;
text-decoration: none;
@@ -298,6 +451,7 @@
padding-top: 5px;
width: 160px;
float: right;
+ margin-right: 16px;
}
/* a) THE FOLLOW BUTTON */
@@ -308,9 +462,22 @@
width:100%;
}
.openerp .oe_followers button.oe_follower.oe_following{
+ color: white;
background-color: #3465A4;
background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
+ background-image: -moz-linear-gradient(top, #729FCF, #3465A4);
+ background-image: -ms-linear-gradient(top, #729FCF, #3465A4);
+ background-image: -o-linear-gradient(top, #729FCF, #3465A4);
+ background-image: linear-gradient(to bottom, #729FCF, #3465A4);
+}
+.openerp .oe_followers button.oe_follower.oe_following:hover{
color: white;
+ background-color: #A21A1A;
+ background-image: -webkit-linear-gradient(top, #DF3F3F, #A21A1A);
+ background-image: -moz-linear-gradient(top, #DF3F3F, #A21A1A);
+ background-image: -ms-linear-gradient(top, #DF3F3F, #A21A1A);
+ background-image: -o-linear-gradient(top, #DF3F3F, #A21A1A);
+ background-image: linear-gradient(to bottom, #DF3F3F, #A21A1A);
}
.openerp .oe_followers button.oe_follower .oe_follow,
@@ -371,12 +538,17 @@
.openerp .oe_record_thread{
display: block;
- margin-right: 180px;
+ margin-left: 16px;
+ margin-right: 212px;
}
/* ----------- INBOX INTEGRATION ----------- */
.openerp .oe_mail_wall .oe_mail{
margin: 16px;
- width: 720px;
+ width: 600px;
+}
+
+.openerp .oe_mail .oe_view_nocontent > p {
+ padding-left: 15px;
}
diff --git a/addons/mail/static/src/css/mail_group.css b/addons/mail/static/src/css/mail_group.css
index b700cb8c80f..3146247c1bc 100644
--- a/addons/mail/static/src/css/mail_group.css
+++ b/addons/mail/static/src/css/mail_group.css
@@ -68,30 +68,31 @@
min-height: 120px;
}
-.oe_group_details a, .oe_group_details a:hover {
- font-weight: bold;
- color: #4c4c4c;
-}
-
.oe_group_details h4 {
margin: 0;
font-size: 13px;
}
-.oe_group_details h4 a {
- color: #4c4c4c;
-}
-
-.oe_group_details h4 a:hover {
- text-decoration: underline;
-}
-
.oe_group_details ul {
margin: 3px 0 5px;
padding: 0;
list-style: none;
}
-.oe_group_details li {
+.openerp .oe_group_details li {
margin: 2px 0;
}
+
+.openerp .oe_group_button {
+ padding-top: 7px;
+}
+
+.openerp .oe_group_button .oe_group_join {
+ color: white;
+ background-color: #3465A4;
+ background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
+ background-image: -moz-linear-gradient(top, #729FCF, #3465A4);
+ background-image: -ms-linear-gradient(top, #729FCF, #3465A4);
+ background-image: -o-linear-gradient(top, #729FCF, #3465A4);
+ background-image: linear-gradient(to bottom, #729FCF, #3465A4);
+}
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index 1eb3c29e556..e214168cb25 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -26,7 +26,8 @@ openerp.mail = function (session) {
'default_use_template', 'default_partner_ids', 'default_model',
'default_res_id', 'default_content_subtype', , 'default_subject',
'default_body', 'active_id', 'lang', 'bin_raw', 'tz',
- 'active_model', 'edi_web_url_view', 'active_ids']
+ 'active_model', 'edi_web_url_view', 'active_ids',
+ 'default_attachment_ids']
for (var key in action.context) {
if (_.indexOf(context_keys, key) == -1) {
action.context[key] = null;
@@ -54,8 +55,8 @@ openerp.mail = function (session) {
mail.ChatterUtils = {
/* Get an image in /web/binary/image?... */
- get_image: function (session, model, field, id) {
- return session.prefix + '/web/binary/image?session_id=' + session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
+ get_image: function (session, model, field, id, resize) {
+ return session.prefix + '/web/binary/image?session_id=' + session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '') + '&resize=' + (resize ? encodeURIComponent(resize) : '');
},
/* Get the url of an attachment {'id': id} */
@@ -111,10 +112,265 @@ openerp.mail = function (session) {
}
return domain;
- }
+ },
+
+ // inserts zero width space between each letter of a string so that
+ // the word will correctly wrap in html boxes smaller than the text
+ breakword: function(str){
+ var out = '';
+ if (!str) {
+ return str;
+ }
+ for(var i = 0, len = str.length; i < len; i++){
+ out += _.str.escapeHTML(str[i]) + '';
+ }
+ return out;
+ },
+
+ // returns the file type of a file based on its extension
+ // As it only looks at the extension it is quite approximative.
+ filetype: function(url){
+ url = url.filename || url;
+ var tokens = url.split('.');
+ if(tokens.length <= 1){
+ return 'unknown';
+ }
+ var extension = tokens[tokens.length -1];
+ if(extension.length === 0){
+ return 'unknown';
+ }else{
+ extension = extension.toLowerCase();
+ }
+ var filetypes = {
+ 'webimage': ['png','jpg','jpeg','jpe','gif'], // those have browser preview
+ 'image': ['tif','tiff','tga',
+ 'bmp','xcf','psd','ppm','pbm','pgm','pnm','mng',
+ 'xbm','ico','icon','exr','webp','psp','pgf','xcf',
+ 'jp2','jpx','dng','djvu','dds'],
+ 'vector': ['ai','svg','eps','vml','cdr','xar','cgm','odg','sxd'],
+ 'print': ['dvi','pdf','ps'],
+ 'document': ['doc','docx','odm','odt'],
+ 'presentation': ['key','keynote','odp','pps','ppt'],
+ 'font': ['otf','ttf','woff','eot'],
+ 'archive': ['zip','7z','ace','apk','bzip2','cab','deb','dmg','gzip','jar',
+ 'rar','tar','gz','pak','pk3','pk4','lzip','lz','rpm'],
+ 'certificate': ['cer','key','pfx','p12','pem','crl','der','crt','csr'],
+ 'audio': ['aiff','wav','mp3','ogg','flac','wma','mp2','aac',
+ 'm4a','ra','mid','midi'],
+ 'video': ['asf','avi','flv','mkv','m4v','mpeg','mpg','mpe','wmv','mp4','ogm'],
+ 'text': ['txt','rtf','ass'],
+ 'html': ['html','xhtml','xml','htm','css'],
+ 'disk': ['iso','nrg','img','ccd','sub','cdi','cue','mds','mdx'],
+ 'script': ['py','js','c','cc','cpp','cs','h','java','bat','sh',
+ 'd','rb','pl','as','cmd','coffee','m','r','vbs','lisp'],
+ 'spreadsheet': ['123','csv','ods','numbers','sxc','xls','vc','xlsx'],
+ 'binary': ['exe','com','bin','app'],
+ };
+ for(filetype in filetypes){
+ var ext_list = filetypes[filetype];
+ for(var i = 0, len = ext_list.length; i < len; i++){
+ if(extension === ext_list[i]){
+ return filetype;
+ }
+ }
+ }
+ return 'unknown';
+ },
+
};
+ /**
+ * ------------------------------------------------------------
+ * MessageCommon
+ * ------------------------------------------------------------
+ *
+ * Common base for expandables, chatter messages and composer. It manages
+ * the various variables common to those models.
+ */
+
+ mail.MessageCommon = session.web.Widget.extend({
+
+ /**
+ * ------------------------------------------------------------
+ * FIXME: this comment was moved as is from the ThreadMessage Init as
+ * part of a refactoring. Check that it is still correct
+ * ------------------------------------------------------------
+ * This widget handles the display of a messages in a thread.
+ * Displays a record and performs some formatting on the record :
+ * - record.date: formatting according to the user timezone
+ * - record.timerelative: relative time givein by timeago lib
+ * - record.avatar: image url
+ * - record.attachment_ids[].url: url of each attachmentThe
+ * thread view :
+ * - root thread
+ * - - sub message (parent_id = root message)
+ * - - - sub thread
+ * - - - - sub sub message (parent id = sub thread)
+ * - - sub message (parent_id = root message)
+ * - - - sub thread
+ */
+
+ init: function (parent, datasets, options) {
+ this._super(parent, options);
+
+ // record options
+ this.options = datasets.options || options || {};
+ // record domain and context
+ this.domain = datasets.domain || options.domain || [];
+ this.context = _.extend({
+ default_model: false,
+ default_res_id: 0,
+ default_parent_id: false }, options.context || {});
+
+ // data of this message
+ this.id = datasets.id || false,
+ this.last_id = this.id,
+ this.model = datasets.model || this.context.default_model || false,
+ this.res_id = datasets.res_id || this.context.default_res_id || false,
+ this.parent_id = datasets.parent_id || false,
+ this.type = datasets.type || false,
+ this.is_author = datasets.is_author || false,
+ this.is_private = datasets.is_private || false,
+ this.subject = datasets.subject || false,
+ this.name = datasets.name || false,
+ this.record_name = datasets.record_name || false,
+ this.body = datasets.body || false,
+ this.vote_nb = datasets.vote_nb || 0,
+ this.has_voted = datasets.has_voted || false,
+ this.is_favorite = datasets.is_favorite || false,
+ this.thread_level = datasets.thread_level || 0,
+ this.to_read = datasets.to_read || false,
+ this.author_id = datasets.author_id || false,
+ this.attachment_ids = datasets.attachment_ids || [],
+ this.partner_ids = datasets.partner_ids || [];
+ this._date = datasets.date;
+
+ this.format_data();
+
+ // record options and data
+ this.show_record_name = this.record_name && !this.thread_level && this.model != 'res.partner';
+ this.options.show_read = false;
+ this.options.show_unread = false;
+ if (this.options.show_read_unread_button) {
+ if (this.options.read_action == 'read') this.options.show_read = true;
+ else if (this.options.read_action == 'unread') this.options.show_unread = true;
+ else {
+ this.options.show_read = this.to_read;
+ this.options.show_unread = !this.to_read;
+ this.options.rerender = true;
+ this.options.toggle_read = true;
+ }
+ }
+ this.parent_thread = parent.messages != undefined ? parent : this.options.root_thread;
+ this.thread = false;
+ },
+
+ /* Convert date, timerelative and avatar in displayable data. */
+ format_data: function () {
+
+ //formating and add some fields for render
+ if (this._date) {
+ this.date = session.web.format_value(this._date, {type:"datetime"});
+ this.timerelative = $.timeago(this.date);
+ }
+ if (this.type == 'email' && (!this.author_id || !this.author_id[0])) {
+ this.avatar = ('/mail/static/src/img/email_icon.png');
+ } else if (this.author_id && this.template != 'mail.compose_message') {
+ this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]);
+ } else {
+ this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
+ }
+
+ },
+
+
+ /* upload the file on the server, add in the attachments list and reload display
+ */
+ display_attachments: function () {
+ for (var l in this.attachment_ids) {
+ var attach = this.attachment_ids[l];
+ if (!attach.formating) {
+ attach.url = mail.ChatterUtils.get_attachment_url(this.session, attach);
+ attach.filetype = mail.ChatterUtils.filetype(attach.filename);
+ attach.name = mail.ChatterUtils.breakword(attach.name || attach.filename);
+ attach.formating = true;
+ }
+ }
+ this.$(".oe_msg_attachment_list").html( session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) );
+ },
+
+ /* return the link to resized image
+ */
+ attachments_resize_image: function (id, resize) {
+ return mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', id, resize);
+ },
+
+ /* get all child message id linked.
+ * @return array of id
+ */
+ get_child_ids: function () {
+ return _.map(this.get_childs(), function (val) { return val.id; });
+ },
+
+ /* get all child message linked.
+ * @return array of message object
+ */
+ get_childs: function (nb_thread_level) {
+ var res=[];
+ if (arguments[1] && this.id) res.push(this);
+ if ((isNaN(nb_thread_level) || nb_thread_level>0) && this.thread) {
+ _(this.thread.messages).each(function (val, key) {
+ res = res.concat( val.get_childs((isNaN(nb_thread_level) ? undefined : nb_thread_level-1), true) );
+ });
+ }
+ return res;
+ },
+
+ /**
+ * search a message in all thread and child thread.
+ * This method return an object message.
+ * @param {object}{int} option.id
+ * @param {object}{string} option.model
+ * @param {object}{boolean} option._go_thread_wall
+ * private for check the top thread
+ * @return thread object
+ */
+ browse_message: function (options) {
+ // goto the wall thread for launch browse
+ if (!options._go_thread_wall) {
+ options._go_thread_wall = true;
+ for (var i in this.options.root_thread.messages) {
+ var res=this.options.root_thread.messages[i].browse_message(options);
+ if (res) return res;
+ }
+ }
+
+ if (this.id==options.id)
+ return this;
+
+ for (var i in this.thread.messages) {
+ if (this.thread.messages[i].thread) {
+ var res=this.thread.messages[i].browse_message(options);
+ if (res) return res;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * call on_message_delete on his parent thread
+ */
+ destroy: function () {
+
+ this._super();
+ this.parent_thread.on_message_detroy(this);
+
+ }
+
+ });
+
/**
* ------------------------------------------------------------
* ComposeMessage widget
@@ -126,7 +382,7 @@ openerp.mail = function (session) {
* When the user focuses the textarea, the compose message is instantiated.
*/
- mail.ThreadComposeMessage = session.web.Widget.extend({
+ mail.ThreadComposeMessage = mail.MessageCommon.extend({
template: 'mail.compose_message',
/**
@@ -138,45 +394,22 @@ openerp.mail = function (session) {
*/
init: function (parent, datasets, options) {
- var self = this;
- this._super(parent);
- this.context = options.context || {};
- this.options = options.options;
-
+ this._super(parent, datasets, options);
this.show_compact_message = false;
-
- // data of this compose message
- this.attachment_ids = [];
- this.id = datasets.id;
- this.model = datasets.model;
- this.res_model = datasets.res_model;
- this.is_private = datasets.is_private || false;
- this.partner_ids = datasets.partner_ids || [];
- this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
- this.thread_level = datasets.thread_level;
- this.parent_thread= parent.messages!= undefined ? parent : false;
-
- this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment');
this.show_delete_attachment = true;
-
- this.fileupload_id = _.uniqueId('oe_fileupload_temp');
- $(window).on(self.fileupload_id, self.on_attachment_loaded);
},
start: function () {
+ this._super.apply(this, arguments);
+
+ this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment');
+ this.fileupload_id = _.uniqueId('oe_fileupload_temp');
+ $(window).on(this.fileupload_id, this.on_attachment_loaded);
+
this.display_attachments();
this.bind_events();
},
- /* upload the file on the server, add in the attachments list and reload display
- */
- display_attachments: function () {
- this.$(".oe_msg_attachment_list").html(
- session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) );
- // event: delete an attachment
- this.$(".oe_msg_attachment_list").on('click', '.oe_mail_attachment_delete', this.on_attachment_delete);
- },
-
/* when a user click on the upload button, send file read on_attachment_loaded
*/
on_attachment_change: function (event) {
@@ -263,24 +496,31 @@ openerp.mail = function (session) {
this.$('textarea.oe_compact').on('focus', _.bind( this.on_compose_expandable, this));
// set the function called when attachments are added
- this.$el.on('change', 'input.oe_form_binary_file', _.bind( this.on_attachment_change, this) );
+ this.$('input.oe_form_binary_file').on('change', _.bind( this.on_attachment_change, this) );
- this.$el.on('click', '.oe_cancel', _.bind( this.on_cancel, this) );
- this.$el.on('click', '.oe_post', _.bind( this.on_message_post, this) );
- this.$el.on('click', '.oe_full', _.bind( this.on_compose_fullmail, this, 'reply') );
+ this.$('.oe_cancel').on('click', _.bind( this.on_cancel, this) );
+ this.$('.oe_post').on('click', _.bind( this.on_message_post, this) );
+ this.$('.oe_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment') );
/* stack for don't close the compose form if the user click on a button */
- this.$el.on('mousedown', '.oe_msg_footer', _.bind( function () { this.stay_open = true; }, this));
- this.$('textarea:not(.oe_compact):first').on('focus, mouseup, keydown', _.bind( function () { this.stay_open = false; }, this));
- this.$('textarea:not(.oe_compact):first').autosize();
+ this.$('.oe_msg_footer').on('mousedown', _.bind( function () { this.stay_open = true; }, this));
+ var ev_stay = {};
+ ev_stay.mouseup = ev_stay.keydown = ev_stay.focus = function () { self.stay_open = false; };
+ this.$('textarea:not(.oe_compact)').on(ev_stay);
+ this.$('textarea:not(.oe_compact)').autosize();
// auto close
- this.$el.on('blur', 'textarea:not(.oe_compact):first', _.bind( this.on_compose_expandable, this));
+ this.$('textarea:not(.oe_compact)').on('blur', _.bind( this.on_compose_expandable, this));
+
+ // event: delete child attachments off the oe_msg_attachment_list box
+ this.$(".oe_msg_attachment_list").on('click', '.oe_delete', this.on_attachment_delete);
},
on_compose_fullmail: function (default_composition_mode) {
if (default_composition_mode == 'reply') {
var context = {
+ 'default_model': this.context.default_model,
+ 'default_res_id': this.context.default_res_id,
'default_composition_mode': default_composition_mode,
'default_parent_id': this.id,
'default_body': mail.ChatterUtils.get_text2html(this.$el ? (this.$el.find('textarea:not(.oe_compact)').val() || '') : ''),
@@ -357,10 +597,15 @@ openerp.mail = function (session) {
this.context.default_parent_id,
attachments,
this.parent_thread.context
- ]).then(function (record) {
+ ]).done(function (record) {
var thread = self.parent_thread;
+
+ if (self.options.display_indented_thread < self.thread_level && thread.parent_message) {
+ thread = thread.parent_message.parent_thread;
+ }
// create object and attach to the thread object
- thread.message_fetch(false, false, [record], function (arg, data) {
+ thread.message_fetch([['id', 'child_of', [self.id]]], false, [record], function (arg, data) {
+ data[0].no_sorted = true;
var message = thread.create_message_object( data[0] );
// insert the message on dom
thread.insert_message( message, self.$el );
@@ -419,30 +664,16 @@ openerp.mail = function (session) {
* - - visible message
* - - expandable
*/
- mail.ThreadExpandable = session.web.Widget.extend({
+ mail.ThreadExpandable = mail.MessageCommon.extend({
template: 'mail.thread.expandable',
- init: function (parent, datasets, context) {
- this._super(parent);
- this.domain = datasets.domain || [];
- this.options = datasets.options;
- this.context = _.extend({
- default_model: 'mail.thread',
- default_res_id: 0,
- default_parent_id: false }, context || {});
-
- // data of this expandable message
- this.id = datasets.id || -1,
- this.model = datasets.model || false,
- this.parent_id = datasets.parent_id || false,
- this.nb_messages = datasets.nb_messages || 0,
- this.thread_level = datasets.thread_level || 0,
- this.type = 'expandable',
- this.max_limit = this.id < 0 || false,
- this.flag_used = false,
- this.parent_thread= parent.messages!= undefined ? parent : this.options._parents[0];
+ init: function (parent, datasets, options) {
+ this._super(parent, datasets, options);
+ this.type = 'expandable';
+ this.max_limit = datasets.max_limit;
+ this.nb_messages = datasets.nb_messages;
+ this.flag_used = false;
},
-
start: function () {
this._super.apply(this, arguments);
@@ -460,7 +691,7 @@ openerp.mail = function (session) {
* Bind events in the widget. Each event is slightly described
* in the function. */
bind_events: function () {
- this.$el.on('click', 'a.oe_msg_fetch_more', this.on_expandable);
+ this.$('.oe_msg_more_message').on('click', this.on_expandable);
},
animated_destroy: function (fadeTime) {
@@ -480,154 +711,36 @@ openerp.mail = function (session) {
}
this.flag_used = true;
- this.animated_destroy(200);
- this.parent_thread.message_fetch(this.domain, this.context);
+ var self = this;
+
+ // read messages
+ self.parent_thread.message_fetch(this.domain, this.context, false, function (arg, data) {
+ // insert the message on dom after this message
+ self.id = false;
+ self.parent_thread.switch_new_message( data, self.$el );
+ self.animated_destroy(200);
+ });
+
return false;
},
- /**
- * call on_message_delete on his parent thread
- */
- destroy: function () {
-
- this._super();
- this.parent_thread.on_message_detroy(this);
-
- }
});
- /**
- * ------------------------------------------------------------
- * Thread Message Widget
- * ------------------------------------------------------------
- * This widget handles the display of a messages in a thread.
- * Displays a record and performs some formatting on the record :
- * - record.date: formatting according to the user timezone
- * - record.timerelative: relative time givein by timeago lib
- * - record.avatar: image url
- * - record.attachment_ids[].url: url of each attachmentThe
- * thread view :
- * - root thread
- * - - sub message (parent_id = root message)
- * - - - sub thread
- * - - - - sub sub message (parent id = sub thread)
- * - - sub message (parent_id = root message)
- * - - - sub thread
- */
- mail.ThreadMessage = session.web.Widget.extend({
+ mail.ThreadMessage = mail.MessageCommon.extend({
template: 'mail.thread.message',
- /**
- * @param {Object} parent parent
- * @param {Array} [domain]
- * @param {Object} [context] context of the thread. It should
- contain at least default_model, default_res_id. Please refer to
- the ComposeMessage widget for more information about it.
- * @param {Object} [options]
- * @param {Object} [thread] read obout mail.Thread object
- * @param {Object} [message]
- * @param {Number} [truncate_limit=250] number of character to
- * display before having a "show more" link; note that the text
- * will not be truncated if it does not have 110% of the parameter
- * @param {Boolean} [show_record_name]
- *... @param {int} [show_reply_button] number thread level to display the reply button
- *... @param {int} [show_read_unread_button] number thread level to display the read/unread button
- */
- init: function (parent, datasets, context) {
- this._super(parent);
-
- // record domain and context
- this.domain = datasets.domain || [];
- this.context = _.extend({
- default_model: 'mail.thread',
- default_res_id: 0,
- default_parent_id: false }, context || {});
-
- // record options
- this.options = datasets.options;
-
- // data of this message
- this.id = datasets.id || -1,
- this.model = datasets.model || false,
- this.parent_id = datasets.parent_id || false,
- this.res_id = datasets.res_id || false,
- this.type = datasets.type || false,
- this.is_author = datasets.is_author || false,
- this.is_private = datasets.is_private || false,
- this.subject = datasets.subject || false,
- this.name = datasets.name || false,
- this.record_name = datasets.record_name || false,
- this.body = datasets.body || false,
- this.vote_nb = datasets.vote_nb || 0,
- this.has_voted = datasets.has_voted || false,
- this.is_favorite = datasets.is_favorite || false,
- this.thread_level = datasets.thread_level || 0,
- this.to_read = datasets.to_read || false,
- this.author_id = datasets.author_id || [],
- this.attachment_ids = datasets.attachment_ids || [],
- this._date = datasets.date;
-
-
- this.show_reply_button = this.options.show_compose_message && this.options.show_reply_button > this.thread_level;
- this.show_read_unread_button = this.options.show_read_unread_button > this.thread_level;
-
- // record options and data
- this.parent_thread= parent.messages!= undefined ? parent : this.options._parents[0];
- this.thread = false;
-
- if ( this.id > 0 ) {
- this.formating_data();
- }
-
- this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
- this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
- this.ds_follow = new session.web.DataSetSearch(this, 'mail.followers');
- },
-
- /* Convert date, timerelative and avatar in displayable data. */
- formating_data: function () {
-
- //formating and add some fields for render
- this.date = session.web.format_value(this._date, {type:"datetime"});
- this.timerelative = $.timeago(this.date);
- if (this.type == 'email') {
- this.avatar = ('/mail/static/src/img/email_icon.png');
- } else {
- this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]);
- }
- for (var l in this.attachment_ids) {
- var attach = this.attachment_ids[l];
- attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
-
- if ((attach.filename || attach.name).match(/[.](jpg|jpg|gif|png|tif|svg)$/i)) {
- attach.is_image = true;
- attach['url'] = mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', attach.id);
- }
- }
- },
start: function () {
this._super.apply(this, arguments);
this.expender();
- this.$el.hide().fadeIn(750, function () {$(this).css('display', '');});
- this.resize_img();
this.bind_events();
if(this.thread_level < this.options.display_indented_thread) {
this.create_thread();
}
this.$('.oe_msg_attachments, .oe_msg_images').addClass("oe_hidden");
- },
- resize_img: function () {
- var resize = function () {
- var h = $(this).height();
- var w = $(this).width();
- if ( h > 100 || w >100 ) {
- var ratio = 100 / (h > w ? h : w);
- $(this).attr("width", parseInt( w*ratio )).attr("height", parseInt( h*ratio ));
- }
- };
- this.$("img").load(resize).each(resize);
+ this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
+ this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
},
/**
@@ -635,30 +748,15 @@ openerp.mail = function (session) {
* in the function. */
bind_events: function () {
var self = this;
+ // header icons bindings
+ this.$('.oe_read').on('click', this.on_message_read);
+ this.$('.oe_unread').on('click', this.on_message_unread);
+ this.$('.oe_msg_delete').on('click', this.on_message_delete);
+ this.$('.oe_reply').on('click', this.on_message_reply);
+ this.$('.oe_star').on('click', this.on_star);
+ this.$('.oe_msg_vote').on('click', this.on_vote);
+ this.$('.oe_view_attachments').on('click', this.on_view_attachments);
- // event: click on 'Attachment(s)' in msg
- this.$('.oe_mail_msg_view_attachments').on('click', function (event) {
- var attach = self.$('.oe_msg_attachments:first, .oe_msg_images:first');
- if ( self.$('.oe_msg_attachments:first').hasClass("oe_hidden") ) {
- attach.removeClass("oe_hidden");
- } else {
- attach.addClass("oe_hidden");
- }
- self.resize_img();
- });
- // event: click on icone 'Read' in header
- this.$el.on('click', '.oe_read', this.on_message_read_unread);
- // event: click on icone 'UnRead' in header
- this.$el.on('click', '.oe_unread', this.on_message_read_unread);
- // event: click on 'Delete' in msg side menu
- this.$el.on('click', '.oe_msg_delete', this.on_message_delete);
-
- // event: click on 'Reply' in msg
- this.$el.on('click', '.oe_reply', this.on_message_reply);
- // event: click on 'Vote' button
- this.$el.on('click', '.oe_msg_vote', this.on_vote);
- // event: click on 'starred/favorite' button
- this.$el.on('click', '.oe_star', this.on_star);
},
/* Call the on_compose_message on the thread of this message. */
@@ -673,7 +771,7 @@ openerp.mail = function (session) {
this.$('.oe_msg_body:first').expander({
slicePoint: this.options.truncate_limit,
expandText: 'read more',
- userCollapseText: '[^]',
+ userCollapseText: 'read less',
detailClass: 'oe_msg_tail',
moreClass: 'oe_mail_expand',
lessClass: 'oe_mail_reduce',
@@ -717,6 +815,17 @@ openerp.mail = function (session) {
}
},
+ /* Call the on_compose_message on the thread of this message. */
+ on_view_attachments:function (event) {
+ event.stopPropagation();
+ var self = this;
+ if (!this.toggle_attachment) {
+ self.display_attachments();
+ this.toggle_attachment = true;
+ }
+ this.$('.oe_msg_attachment_list').toggle(200);
+ },
+
/**
* Wait a confirmation for delete the message on the DB.
* Make an animate destroy
@@ -732,81 +841,90 @@ openerp.mail = function (session) {
return false;
},
+ /* Check if the message must be destroy and detroy it or check for re render widget
+ * @param {callback} apply function
+ */
+ check_for_rerender: function () {
+ var self = this;
+
+ var messages = [this].concat(this.get_childs());
+ var message_ids = _.map(messages, function (msg) { return msg.id;});
+ var domain = mail.ChatterUtils.expand_domain( this.options.root_thread.domain )
+ .concat([["id", "in", message_ids ]]);
+
+ return this.parent_thread.ds_message.call('message_read', [undefined, domain, [], !!this.parent_thread.options.display_indented_thread, this.context, this.parent_thread.id])
+ .then( function (records) {
+ // remove message not loaded
+ _.map(messages, function (msg) {
+ if(!_.find(records, function (record) { return record.id == msg.id; })) {
+ msg.animated_destroy(150);
+ } else {
+ msg.renderElement();
+ msg.start()
+ }
+ });
+
+ });
+ },
+
+ on_message_read: function (event) {
+ event.stopPropagation();
+ this.on_message_read_unread(true);
+ return false;
+ },
+
+ on_message_unread: function (event) {
+ event.stopPropagation();
+ this.on_message_read_unread(false);
+ return false;
+ },
+
/*The selected thread and all childs (messages/thread) became read
* @param {object} mouse envent
*/
- on_message_read_unread: function (event) {
- event.stopPropagation();
- var self=this;
+ on_message_read_unread: function (read_value) {
+ var self = this;
+ var messages = [this].concat(this.get_childs());
- if ( (this.to_read && this.options.typeof_thread == 'inbox') ||
- (!this.to_read && this.options.typeof_thread == 'archives')) {
- this.animated_destroy(150);
- }
-
- // if this message is read, all childs message display is read
- this.ds_notification.call('set_message_read', [ [this.id].concat( this.get_child_ids() ) , this.to_read, this.context]).pipe(function () {
- self.$el.removeClass(self.to_read ? 'oe_msg_unread':'oe_msg_read').addClass(self.to_read ? 'oe_msg_read':'oe_msg_unread');
- self.to_read = !self.to_read;
- });
- return false;
- },
-
- /**
- * search a message in all thread and child thread.
- * This method return an object message.
- * @param {object}{int} option.id
- * @param {object}{string} option.model
- * @param {object}{boolean} option._go_thread_wall
- * private for check the top thread
- * @return thread object
- */
- browse_message: function (options) {
- // goto the wall thread for launch browse
- if (!options._go_thread_wall) {
- options._go_thread_wall = true;
- for (var i in this.options._parents[0].messages) {
- var res=this.options._parents[0].messages[i].browse_message(options);
- if (res) return res;
- }
- }
-
- if (this.id==options.id)
- return this;
-
- for (var i in this.thread.messages) {
- if (this.thread.messages[i].thread) {
- var res=this.thread.messages[i].browse_message(options);
- if (res) return res;
+ // inside the inbox, when the user mark a message as read/done, don't apply this value
+ // for the stared/favorite message
+ if (this.options.view_inbox && read_value) {
+ var messages = _.filter(messages, function (val) { return !val.is_favorite && val.id; });
+ if (!messages.length) {
+ this.check_for_rerender();
+ return false;
}
}
+ var message_ids = _.map(messages, function (val) { return val.id; });
+ this.ds_notification.call('set_message_read', [message_ids, read_value, this.context])
+ .then(function () {
+ // apply modification
+ _.each(messages, function (msg) {
+ msg.to_read = !read_value;
+ if (msg.options.toggle_read) {
+ msg.options.show_read = msg.to_read;
+ msg.options.show_unread = !msg.to_read;
+ }
+ });
+ // check if the message must be display, destroy or rerender
+ self.check_for_rerender();
+ });
return false;
},
- /* get all child message id linked.
- * @return array of id
- */
- get_child_ids: function () {
- var res=[]
- if (arguments[0]) res.push(this.id);
- if (this.thread) {
- res = res.concat( this.thread.get_child_ids(true) );
- }
- return res;
- },
-
/**
* add or remove a vote for a message and display the result
*/
on_vote: function (event) {
event.stopPropagation();
- var self=this;
- return this.ds_message.call('vote_toggle', [[self.id]]).pipe(function (vote) {
- self.has_voted = vote;
- self.vote_nb += self.has_voted ? 1 : -1;
- self.display_vote();
- });
+ this.ds_message.call('vote_toggle', [[this.id]])
+ .then(
+ _.bind(function (vote) {
+ this.has_voted = vote;
+ this.vote_nb += this.has_voted ? 1 : -1;
+ this.display_vote();
+ }, this));
return false;
},
@@ -814,10 +932,10 @@ openerp.mail = function (session) {
* Display the render of this message's vote
*/
display_vote: function () {
- var self = this;
- var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': self});
- self.$(".oe_msg_vote:first").remove();
- self.$(".oe_mail_vote_count:first").replaceWith(vote_element);
+ var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': this});
+ this.$(".oe_msg_footer:first .oe_mail_vote_count").remove();
+ this.$(".oe_msg_footer:first .oe_msg_vote").replaceWith(vote_element);
+ this.$('.oe_msg_vote').on('click', this.on_vote);
},
/**
@@ -827,34 +945,29 @@ openerp.mail = function (session) {
event.stopPropagation();
var self=this;
var button = self.$('.oe_star:first');
- return this.ds_message.call('favorite_toggle', [[self.id]]).pipe(function (star) {
- self.is_favorite=star;
- if (self.is_favorite) {
- button.addClass('oe_starred');
- } else {
- button.removeClass('oe_starred');
- if ( self.options.typeof_thread == 'stared' ) {
- self.animated_destroy(150);
+
+ this.ds_message.call('favorite_toggle', [[self.id]])
+ .then(function (star) {
+ self.is_favorite=star;
+ if (self.is_favorite) {
+ button.addClass('oe_starred');
+ } else {
+ button.removeClass('oe_starred');
}
- }
- });
+
+ if (self.options.view_inbox && self.is_favorite) {
+ self.on_message_read_unread(true);
+ } else {
+ self.check_for_rerender();
+ }
+ });
return false;
},
- /**
- * call on_message_delete on his parent thread
- */
- destroy: function () {
-
- this._super();
- this.parent_thread.on_message_detroy(this);
-
- }
-
});
/**
- * ------------------------------------------------------------
+ * ------------------------------------------------------------
* Thread Widget
* ------------------------------------------------------------
*
@@ -881,14 +994,11 @@ openerp.mail = function (session) {
* @param {Object} [thread]
* @param {int} [display_indented_thread] number thread level to indented threads.
* other are on flat mode
- * @param {Select} [typeof_thread] inbox/archives/stared/sent
- * type of thread and option for user application like animate
- * destroy for read/unread
* @param {Array} [parents] liked with the parents thread
* use with browse, fetch... [O]= top parent
*/
init: function (parent, datasets, options) {
- this._super(parent);
+ this._super(parent, options);
this.domain = options.domain || [];
this.context = _.extend({
default_model: 'mail.thread',
@@ -896,21 +1006,24 @@ openerp.mail = function (session) {
default_parent_id: false }, options.context || {});
this.options = options.options;
- this.options._parents = (options.options._parents != undefined ? options.options._parents : []).concat( [this] );
-
+ this.options.root_thread = (options.options.root_thread != undefined ? options.options.root_thread : this);
+ this.options.show_compose_message = this.options.show_compose_message && (this.options.display_indented_thread >= this.thread_level || !this.thread_level);
+
// record options and data
this.parent_message= parent.thread!= undefined ? parent : false ;
// data of this thread
this.id = datasets.id || false,
- this.model = datasets.model || false,
+ this.last_id = datasets.last_id || false,
this.parent_id = datasets.parent_id || false,
+
this.is_private = datasets.is_private || false,
this.author_id = datasets.author_id || false,
this.thread_level = (datasets.thread_level+1) || 0,
this.partner_ids = _.filter(datasets.partner_ids, function (partner) { return partner[0]!=datasets.author_id[0]; } )
this.messages = [];
- this.show_compose_message = this.options.show_compose_message && (this.options.show_reply_button > this.thread_level || !this.thread_level);
+
+ this.options.flat_mode = !!(this.options.display_indented_thread > this.thread_level ? this.options.display_indented_thread - this.thread_level : 0);
// object compose message
this.compose_message = false;
@@ -934,13 +1047,10 @@ openerp.mail = function (session) {
'context': this.context,
'options': this.options,
});
- if (!this.thread_level) {
- // root view
+ if (!this.thread_level || this.thread_level > this.options.display_indented_thread) {
this.compose_message.insertBefore(this.$el);
- } else if (this.thread_level > this.options.display_indented_thread) {
- this.compose_message.insertAfter(this.$el);
} else {
- this.compose_message.appendTo(this.$el);
+ this.compose_message.prependTo(this.$el);
}
}
},
@@ -948,21 +1058,20 @@ openerp.mail = function (session) {
/* When the expandable object is visible on screen (with scrolling)
* then the on_expandable function is launch
*/
- on_scroll: function (event) {
- if (event)event.stopPropagation();
- this.$('.oe_msg_expandable:last');
-
- var message = this.messages[this.messages.length-1];
- if (message && message.type=="expandable" && message.max_limit) {
- var pos = message.$el.position();
+ on_scroll: function () {
+ var expandables =
+ _.each( _.filter(this.messages, function (val) {return val.max_limit && !val.parent_id;}), function (val) {
+ var pos = val.$el.position();
if (pos.top) {
/* bottom of the screen */
var bottom = $(window).scrollTop()+$(window).height()+200;
if (bottom > pos.top) {
- message.on_expandable();
+ val.on_expandable();
+ // load only one time
+ val.loading = true;
}
}
- }
+ });
},
/**
@@ -970,8 +1079,8 @@ openerp.mail = function (session) {
* in the function. */
bind_events: function () {
var self = this;
- self.$el.on('click', '.oe_mail_list_recipients .oe_more', self.on_show_recipients);
- self.$el.on('click', '.oe_mail_compose_textarea .oe_more_hidden', self.on_hide_recipients);
+ self.$('.oe_mail_list_recipients .oe_more').on('click', self.on_show_recipients);
+ self.$('.oe_mail_compose_textarea .oe_more_hidden').on('click', self.on_hide_recipients);
},
/**
@@ -981,6 +1090,7 @@ openerp.mail = function (session) {
var p=$(this).parent();
p.find('.oe_more_hidden, .oe_hidden').show();
p.find('.oe_more').hide();
+ return false;
},
/**
@@ -990,15 +1100,14 @@ openerp.mail = function (session) {
var p=$(this).parent();
p.find('.oe_more_hidden, .oe_hidden').hide();
p.find('.oe_more').show();
+ return false;
},
/* get all child message/thread id linked.
* @return array of id
*/
get_child_ids: function () {
- var res=[];
- _(this.get_childs()).each(function (val, key) { res.push(val.id); });
- return res;
+ return _.map(this.get_childs(), function (val) { return val.id; });
},
/* get all child message/thread linked.
@@ -1033,17 +1142,17 @@ openerp.mail = function (session) {
// goto the wall thread for launch browse
if (!options._go_thread_wall) {
options._go_thread_wall = true;
- return this.options._parents[0].browse_thread(options);
+ return this.options.root_thread.browse_thread(options);
}
- if (this.id==options.id) {
+ if (this.id == options.id) {
return this;
}
if (options.id) {
for (var i in this.messages) {
if (this.messages[i].thread) {
- var res=this.messages[i].thread.browse_thread({'id':options.id, '_go_thread_wall':true});
+ var res = this.messages[i].thread.browse_thread({'id':options.id, '_go_thread_wall':true});
if (res) return res;
}
}
@@ -1067,8 +1176,8 @@ openerp.mail = function (session) {
* @return message object
*/
browse_message: function (options) {
- if (this.options._parents[0].messages[0])
- return this.options._parents[0].messages[0].browse_message(options);
+ if (this.options.root_thread.messages[0])
+ return this.options.root_thread.messages[0].browse_message(options);
},
/**
@@ -1079,6 +1188,7 @@ openerp.mail = function (session) {
on_compose_message: function () {
this.instantiate_compose_message();
this.compose_message.on_compose_expandable();
+ return false;
},
/**
@@ -1086,8 +1196,8 @@ openerp.mail = function (session) {
*/
no_message: function () {
var no_message = $(session.web.qweb.render('mail.wall_no_message', {}));
- if (this.options.no_message) {
- no_message.html(this.options.no_message);
+ if (this.options.help) {
+ no_message.html(this.options.help);
}
no_message.appendTo(this.$el);
},
@@ -1101,18 +1211,20 @@ openerp.mail = function (session) {
* @param {Array} ids read (if the are some ids, the method don't use the domain)
*/
message_fetch: function (replace_domain, replace_context, ids, callback) {
- var self = this;
-
- // domain and context: options + additional
- fetch_domain = replace_domain ? replace_domain : this.domain;
- fetch_context = replace_context ? replace_context : this.context;
- var message_loaded_ids = this.id ? [this.id].concat( self.get_child_ids() ) : self.get_child_ids();
-
- // CHM note : option for sending in flat mode by server
- var thread_level = this.options.display_indented_thread > this.thread_level ? this.options.display_indented_thread - this.thread_level : 0;
-
- return this.ds_message.call('message_read', [ids, fetch_domain, message_loaded_ids, thread_level, fetch_context, this.context.default_parent_id || undefined])
- .then(callback ? _.bind(callback, this, arguments) : this.proxy('switch_new_message'));
+ return this.ds_message.call('message_read', [
+ // ids force to read
+ ids == false ? undefined : ids,
+ // domain + additional
+ (replace_domain ? replace_domain : this.domain),
+ // ids allready loaded
+ (this.id ? [this.id].concat( this.get_child_ids() ) : this.get_child_ids()),
+ // option for sending in flat mode by server
+ this.options.flat_mode,
+ // context + additional
+ (replace_context ? replace_context : this.context),
+ // parent_id
+ this.context.default_parent_id || undefined
+ ]).done(callback ? _.bind(callback, this, arguments) : this.proxy('switch_new_message'));
},
/**
@@ -1128,22 +1240,23 @@ openerp.mail = function (session) {
data.options = _.extend(self.options, data.options);
if (data.type=='expandable') {
- var message = new mail.ThreadExpandable(self, data, {
+ var message = new mail.ThreadExpandable(self, data, {'context':{
'default_model': data.model || self.context.default_model,
'default_res_id': data.res_id || self.context.default_res_id,
'default_parent_id': self.id,
- });
+ }});
} else {
- var message = new mail.ThreadMessage(self, data, {
+ var message = new mail.ThreadMessage(self, data, {'context':{
'default_model': data.model,
'default_res_id': data.res_id,
'default_parent_id': data.id,
- });
+ }});
}
// check if the message is already create
for (var i in self.messages) {
if (self.messages[i] && self.messages[i].id == message.id) {
+ console.log('Reload message', message.id);
self.messages[i].destroy();
}
}
@@ -1163,74 +1276,18 @@ openerp.mail = function (session) {
*/
insert_message: function (message, dom_insert_after) {
var self=this;
-
- if (this.show_compose_message && this.options.show_compact_message) {
+ if (this.options.show_compact_message > this.thread_level) {
this.instantiate_compose_message();
this.compose_message.do_show_compact();
}
- this.$('.oe_wall_no_message').remove();
+ this.$('.oe_view_nocontent').remove();
if (dom_insert_after) {
message.insertAfter(dom_insert_after);
- return message
- }
-
- // check older and newer message for insertion
- var message_newer = false;
- var message_older = false;
- if (message.id > 0) {
- for (var i in self.messages) {
- if (self.messages[i].id > message.id) {
- if (!message_newer || message_newer.id > self.messages[i].id) {
- message_newer = self.messages[i];
- }
- } else if (self.messages[i].id > 0 && self.messages[i].id < message.id) {
- if (!message_older || message_older.id < self.messages[i].id) {
- message_older = self.messages[i];
- }
- }
- }
- }
-
- var sort = (!!self.thread_level || message.id<0);
-
- if (sort) {
- if (message_older) {
-
- message.insertAfter(message_older.thread ? (message_older.thread.compose_message ? message_older.thread.compose_message.$el : message_older.thread.$el) : message_older.$el);
-
- } else if (message_newer) {
-
- message.insertBefore(message_newer.$el);
-
- } else if (message.id < 0) {
-
- message.appendTo(self.$el);
-
- } else {
-
- message.prependTo(self.$el);
- }
} else {
- if (message_older) {
-
- message.insertBefore(message_older.$el);
-
- } else if (message_newer) {
-
- message.insertAfter(message_newer.thread ? (message_newer.thread.compose_message ? message_newer.thread.compose_message.$el : message_newer.thread.$el) : message_newer.$el );
-
- } else if (message.id < 0) {
-
- message.prependTo(self.$el);
-
- } else {
-
- message.appendTo(self.$el);
-
- }
+ message.appendTo(self.$el);
}
return message
@@ -1241,7 +1298,7 @@ openerp.mail = function (session) {
* Each message is send to his parent object (or parent thread flat mode) for creating the object message.
* @param : {Array} datas from calling RPC to "message_read"
*/
- switch_new_message: function (records) {
+ switch_new_message: function (records, dom_insert_after) {
var self=this;
_(records).each(function (record) {
var thread = self.browse_thread({
@@ -1251,7 +1308,7 @@ openerp.mail = function (session) {
// create object and attach to the thread object
var message = thread.create_message_object( record );
// insert the message on dom
- thread.insert_message( message );
+ thread.insert_message( message, typeof dom_insert_after == 'object' ? dom_insert_after : false);
});
},
@@ -1262,6 +1319,10 @@ openerp.mail = function (session) {
on_message_detroy: function (message) {
this.messages = _.filter(this.messages, function (val) { return !val.isDestroyed(); });
+ if (this.options.root_thread == this && !this.messages.length) {
+ this.no_message();
+ }
+ return false;
},
@@ -1323,7 +1384,6 @@ openerp.mail = function (session) {
} else {
// create a expandable message
var expandable = new mail.ThreadExpandable(this, {
- 'id': message.id,
'model': message.model,
'parent_id': message.parent_id,
'nb_messages': 1,
@@ -1332,9 +1392,11 @@ openerp.mail = function (session) {
'domain': message_dom,
'options': message.options,
}, {
- 'default_model': message.model || this.context.default_model,
- 'default_res_id': message.res_id || this.context.default_res_id,
- 'default_parent_id': this.id,
+ 'context':{
+ 'default_model': message.model || this.context.default_model,
+ 'default_res_id': message.res_id || this.context.default_res_id,
+ 'default_parent_id': this.id,
+ }
});
// add object on array and DOM
@@ -1350,7 +1412,7 @@ openerp.mail = function (session) {
});
/**
- * ------------------------------------------------------------
+ * ------------------------------------------------------------
* mail : root Widget
* ------------------------------------------------------------
*
@@ -1360,7 +1422,7 @@ openerp.mail = function (session) {
*/
session.web.client_actions.add('mail.Widget', 'session.mail.Widget');
mail.Widget = session.web.Widget.extend({
- template: 'mail.Widget',
+ template: 'mail.Root',
/**
* @param {Object} parent parent
@@ -1368,15 +1430,13 @@ openerp.mail = function (session) {
* @param {Object} [context] context of the thread. It should
* contain at least default_model, default_res_id. Please refer to
* the compose_message widget for more information about it.
- * ... @param {Select} [typeof_thread=(mail|stared|archives|send|other)]
- * options for destroy message when the user click on a button
* @param {Object} [options]
*... @param {Number} [truncate_limit=250] number of character to
* display before having a "show more" link; note that the text
* will not be truncated if it does not have 110% of the parameter
*... @param {Boolean} [show_record_name] display the name and link for do action
- *... @param {int} [show_reply_button] number thread level to display the reply button
- *... @param {int} [show_read_unread_button] number thread level to display the read/unread button
+ *... @param {boolean} [show_reply_button] display the reply button
+ *... @param {boolean} [show_read_unread_button] display the read/unread button
*... @param {int} [display_indented_thread] number thread level to indented threads.
* other are on flat mode
*... @param {Boolean} [show_compose_message] allow to display the composer
@@ -1387,35 +1447,25 @@ openerp.mail = function (session) {
* @param {String} [no_message] Message to display when there are no message
*/
init: function (parent, action) {
- var options = action.params || {};
- this._super(parent);
- this.domain = options.domain || [];
- this.context = options.context || {};
- this.search_results = {'domain': [], 'context': {}, 'groupby': {}};
+ this._super(parent, action);
+ var self = this;
+ this.action = _.clone(action);
+ this.domain = this.action.domain || this.action.params.domain || [];
+ this.context = this.action.context || this.action.params.context || {};
- this.options = _.extend({
- 'typeof_thread' : 'inbox',
+ this.action.params = _.extend({
'display_indented_thread' : -1,
- 'show_reply_button' : -1,
- 'show_read_unread_button' : -1,
+ 'show_reply_button' : false,
+ 'show_read_unread_button' : false,
'truncate_limit' : 250,
'show_record_name' : false,
'show_compose_message' : false,
'show_compact_message' : false,
+ 'view_inbox': false,
'message_ids': undefined,
- 'no_message': false
- }, options);
+ }, this.action.params);
- if (this.display_indented_thread === false) {
- this.display_indented_thread = -1;
- }
- if (this.show_reply_button === false) {
- this.show_reply_button = -1;
- }
- if (this.show_read_unread_button === false) {
- this.show_read_unread_button = -1;
- }
-
+ this.action.params.help = this.action.help || false;
},
start: function (options) {
@@ -1423,7 +1473,6 @@ openerp.mail = function (session) {
this.message_render();
this.bind_events();
},
-
/**
*Create the root thread and display this object in the DOM.
@@ -1435,35 +1484,32 @@ openerp.mail = function (session) {
this.thread = new mail.Thread(this, {}, {
'domain' : this.domain,
'context' : this.context,
- 'options': this.options,
+ 'options': this.action.params,
});
this.thread.appendTo( this.$el );
this.thread.no_message();
- this.thread.message_fetch(null, null, this.options.message_ids);
- if (this.options.show_compose_message) {
+ if (this.action.params.show_compose_message) {
this.thread.instantiate_compose_message();
- if (this.options.show_compact_message) {
- this.thread.compose_message.do_show_compact();
- } else {
- this.thread.compose_message.do_hide_compact();
- }
+ this.thread.compose_message.do_show_compact();
}
+
+ this.thread.message_fetch(null, null, this.action.params.message_ids);
+
},
bind_events: function () {
- if (this.context['typeof_thread']!='other') {
- $(document).scroll( this.thread.on_scroll );
- $(window).resize( this.thread.on_scroll );
- window.setTimeout( this.thread.on_scroll, 500 );
- }
+ $(document).scroll( _.bind(this.thread.on_scroll, this.thread) );
+ $(window).resize( _.bind(this.thread.on_scroll, this.thread) );
+ this.$el.resize( _.bind(this.thread.on_scroll, this.thread) );
+ window.setTimeout( _.bind(this.thread.on_scroll, this.thread), 500 );
}
});
/**
- * ------------------------------------------------------------
+ * ------------------------------------------------------------
* mail_thread Widget
* ------------------------------------------------------------
*
@@ -1476,10 +1522,19 @@ openerp.mail = function (session) {
mail.RecordThread = session.web.form.AbstractField.extend({
template: 'mail.record_thread',
- init: function () {
+ init: function (parent, node) {
this._super.apply(this, arguments);
- this.options.domain = this.options.domain || [];
- this.options.context = {'default_model': 'mail.thread', 'default_res_id': false};
+ this.node = _.clone(node);
+
+ this.node.params = _.extend({
+ 'display_indented_thread': -1,
+ 'show_reply_button': false,
+ 'show_read_unread_button': false,
+ 'show_compose_message': this.view.is_action_enabled('edit'),
+ 'show_compact_message': 1,
+ }, this.node.params);
+
+ this.domain = this.node.params && this.node.params.domain || [];
},
start: function () {
@@ -1492,42 +1547,32 @@ openerp.mail = function (session) {
_check_visibility: function () {
this.$el.toggle(this.view.get("actual_mode") !== "create");
},
+
render_value: function () {
var self = this;
+
if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
this.$('oe_mail_thread').hide();
return;
}
- // update context
- _.extend(this.options.context, {
- default_res_id: this.view.datarecord.id,
- default_model: this.view.model,
- default_is_private: false });
- // update domain
- var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]);
- var show_compose_message = this.view.is_action_enabled('edit') ||
- (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
-
- var message_ids = this.getParent().fields.message_ids && this.getParent().fields.message_ids.get_value();
+ this.node.params = _.extend({
+ 'message_ids': this.getParent().fields.message_ids ? this.getParent().fields.message_ids.get_value() : undefined,
+ }, this.node.params);
+ this.node.context = {
+ 'default_res_id': this.view.datarecord.id || false,
+ 'default_model': this.view.model || false,
+ };
if (this.root) {
+ $(' ').insertAfter(this.root.$el);
this.root.destroy();
}
// create and render Thread widget
- this.root = new mail.Widget(this, { params: {
- 'domain' : domain,
- 'context' : this.options.context,
- 'typeof_thread': this.options.context['typeof_thread'] || 'other',
- 'display_indented_thread': -1,
- 'show_reply_button': 0,
- 'show_read_unread_button': -1,
- 'show_compose_message': show_compose_message,
- 'message_ids': message_ids,
- 'show_compact_message': true,
- 'no_message': this.node.attrs.help
- }}
- );
+ this.root = new mail.Widget(this, _.extend(this.node, {
+ 'domain' : (this.domain || []).concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]),
+
+ }));
return this.root.replace(this.$('.oe_mail-placeholder'));
},
@@ -1535,7 +1580,7 @@ openerp.mail = function (session) {
/**
- * ------------------------------------------------------------
+ * ------------------------------------------------------------
* Wall Widget
* ------------------------------------------------------------
*
@@ -1543,6 +1588,7 @@ openerp.mail = function (session) {
* use is to receive a context and a domain, and to delegate the message
* fetching and displaying to the Thread widget.
*/
+
session.web.client_actions.add('mail.wall', 'session.mail.Wall');
mail.Wall = session.web.Widget.extend({
template: 'mail.wall',
@@ -1555,35 +1601,53 @@ openerp.mail = function (session) {
* contain default_model, default_res_id, to give it to the threads.
*/
init: function (parent, action) {
- this._super(parent);
- var options = action.params || {};
- this.options = options;
- this.options.domain = options.domain || [];
- this.options.context = options.context || {};
- this.search_results = {'domain': [], 'context': {}, 'groupby': {}}
- this.ds_msg = new session.web.DataSetSearch(this, 'mail.message');
+ this._super(parent, action);
+
+ this.action = _.clone(action);
+ this.domain = this.action.params.domain || this.action.domain || [];
+ this.context = this.action.params.context || this.action.context || {};
+
+ this.defaults = {};
+ for (var key in this.context) {
+ if (key.match(/^search_default_/)) {
+ this.defaults[key.replace(/^search_default_/, '')] = this.context[key];
+ }
+ }
+
+ this.action.params = _.extend({
+ 'display_indented_thread': 1,
+ 'show_reply_button': true,
+ 'show_read_unread_button': true,
+ 'show_compose_message': true,
+ 'show_compact_message': this.action.params.view_mailbox ? false : 1,
+ 'view_inbox': false,
+ }, this.action.params);
},
start: function () {
- this._super.apply(this, arguments);
- var searchview_ready = this.load_searchview({}, false);
- var thread_displayed = this.message_render();
- this.options.domain = this.options.domain.concat(this.search_results['domain']);
+ this._super.apply(this);
this.bind_events();
- return $.when(searchview_ready, thread_displayed);
+ var searchview_loaded = this.load_searchview(this.defaults);
+ if (! this.searchview.has_defaults) {
+ this.message_render();
+ }
+
},
/**
* Load the mail.message search view
* @param {Object} defaults ??
- * @param {Boolean} hidden some kind of trick we do not care here
*/
- load_searchview: function (defaults, hidden) {
+ load_searchview: function (defaults) {
var self = this;
- this.searchview = new session.web.SearchView(this, this.ds_msg, false, defaults || {}, hidden || false);
- return this.searchview.appendTo(this.$('.oe_view_manager_view_search')).then(function () {
- self.searchview.on('search_data', self, self.do_searchview_search);
- });
+ var ds_msg = new session.web.DataSetSearch(this, 'mail.message');
+ this.searchview = new session.web.SearchView(this, ds_msg, false, defaults || {}, false);
+ this.searchview.appendTo(this.$('.oe_view_manager_view_search'))
+ .then(function () { self.searchview.on('search_data', self, self.do_searchview_search); });
+ if (this.searchview.has_defaults) {
+ this.searchview.ready.then(this.searchview.do_search);
+ }
+ return this.searchview
},
/**
@@ -1600,32 +1664,25 @@ openerp.mail = function (session) {
contexts: contexts || [],
group_by_seq: groupbys || []
}).then(function (results) {
- self.search_results['context'] = results.context;
- self.search_results['domain'] = results.domain;
- self.root.destroy();
- return self.message_render();
+ if(self.root) {
+ $(' ').insertAfter(self.root.$el);
+ self.root.destroy();
+ }
+ return self.message_render(results);
});
},
-
/**
- *Create the root thread widget and display this object in the DOM
- */
+ * Create the root thread widget and display this object in the DOM
+ */
message_render: function (search) {
- var domain = this.options.domain.concat(this.search_results['domain']);
- var context = _.extend(this.options.context, search&&search.search_results['context'] ? search.search_results['context'] : {});
- this.root = new mail.Widget(this, { params: {
+ var domain = this.domain.concat(search && search['domain'] ? search['domain'] : []);
+ var context = _.extend(this.context, search && search['context'] ? search['context'] : {});
+
+ this.root = new mail.Widget(this, _.extend(this.action, {
'domain' : domain,
'context' : context,
- 'typeof_thread': context['typeof_thread'] || 'other',
- 'display_indented_thread': 1,
- 'show_reply_button': 10,
- 'show_read_unread_button': 11,
- 'show_compose_message': true,
- 'show_compact_message': false,
- }}
- );
-
+ }));
return this.root.replace(this.$('.oe_mail-placeholder'));
},
@@ -1653,19 +1710,18 @@ openerp.mail = function (session) {
/**
- * ------------------------------------------------------------
+ * ------------------------------------------------------------
* UserMenu
* ------------------------------------------------------------
*
* Add a link on the top user bar for write a full mail
*/
session.web.ComposeMessageTopButton = session.web.Widget.extend({
- template:'mail.compose_message.button_top_bar',
+ template:'mail.ComposeMessageTopButton',
- start: function (parent, params) {
- var self = this;
- this.$el.on('click', 'button', self.on_compose_message );
- this._super(parent, params);
+ start: function () {
+ this.$('button').on('click', this.on_compose_message );
+ this._super();
},
on_compose_message: function (event) {
@@ -1675,26 +1731,22 @@ openerp.mail = function (session) {
res_model: 'mail.compose.message',
view_mode: 'form',
view_type: 'form',
- action_from: 'mail.ThreadComposeMessage',
views: [[false, 'form']],
target: 'new',
- context: {
- 'default_model': '',
- 'default_res_id': false,
- 'default_content_subtype': 'html',
- },
+ context: { 'default_content_subtype': 'html' },
};
session.client.action_manager.do_action(action);
},
-
});
- session.web.UserMenu = session.web.UserMenu.extend({
- start: function (parent, params) {
- var render = new session.web.ComposeMessageTopButton();
- render.insertAfter(this.$el);
- this._super(parent, params);
- }
+ session.web.UserMenu.include({
+ do_update: function(){
+ var self = this;
+ this._super.apply(this, arguments);
+ this.update_promise.then(function() {
+ var mail_button = new session.web.ComposeMessageTopButton();
+ mail_button.appendTo(session.webclient.$el.find('.oe_systray'));
+ });
+ },
});
-
};
diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js
index a13d2048bf3..7bb019cde4f 100644
--- a/addons/mail/static/src/js/mail_followers.js
+++ b/addons/mail/static/src/js/mail_followers.js
@@ -30,6 +30,8 @@ openerp_mail_followers = function(session, mail) {
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');
+
+ this.value = [];
},
start: function() {
@@ -41,6 +43,11 @@ openerp_mail_followers = function(session, mail) {
this._super();
},
+ set_value: function(_value) {
+ this.value = _value;
+ this._super(_value);
+ },
+
_check_visibility: function() {
this.$el.toggle(this.view.get("actual_mode") !== "create");
},
@@ -85,22 +92,23 @@ openerp_mail_followers = function(session, mail) {
read_value: function () {
var self = this;
- return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).pipe(function (results) {
- self.set_value(results[0].message_follower_ids);
+ return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).then(function (results) {
+ self.value = results[0].message_follower_ids;
+ self.render_value();
});
},
render_value: function () {
this.reinit();
- return this.fetch_followers(this.get("value"));
+ return this.fetch_followers(this.value);
},
fetch_followers: function (value_) {
this.value = value_ || {};
return this.ds_follow.call('read', [this.value, ['name', 'user_ids']])
- .pipe(this.proxy('display_followers'), this.proxy('fetch_generic'))
- .pipe(this.proxy('display_buttons'))
- .pipe(this.proxy('fetch_subtypes'));
+ .then(this.proxy('display_followers'), this.proxy('fetch_generic'))
+ .then(this.proxy('display_buttons'))
+ .then(this.proxy('fetch_subtypes'));
},
/** Read on res.partner failed: fall back on a generic case
@@ -109,10 +117,10 @@ openerp_mail_followers = function(session, mail) {
fetch_generic: function (error, event) {
var self = this;
event.preventDefault();
- return this.ds_users.call('read', [this.session.uid, ['partner_id']]).pipe(function (results) {
+ return this.ds_users.call('read', [this.session.uid, ['partner_id']]).then(function (results) {
var pid = results['partner_id'][0];
- self.message_is_follower = (_.indexOf(self.get('value'), pid) != -1);
- }).pipe(self.proxy('display_generic'));
+ self.message_is_follower = (_.indexOf(self.value, pid) != -1);
+ }).then(self.proxy('display_generic'));
},
_format_followers: function(count){
// TDE note: why redefining _t ?
@@ -131,7 +139,7 @@ openerp_mail_followers = function(session, mail) {
display_generic: function () {
var self = this;
var node_user_list = this.$('.oe_follower_list').empty();
- this.$('.oe_follower_title').html(this._format_followers(this.get('value').length));
+ this.$('.oe_follower_title').html(this._format_followers(this.value.length));
},
/** Display the followers */
@@ -179,7 +187,7 @@ openerp_mail_followers = function(session, mail) {
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'));
+ this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).then(this.proxy('display_subtypes'));
},
/** Display subtypes: {'name': default, followed} */
@@ -206,7 +214,8 @@ openerp_mail_followers = function(session, mail) {
$(record).attr('checked',false);
});
var context = new session.web.CompoundContext(this.build_context(), {});
- return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context]).pipe(this.proxy('read_value'));
+ return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context])
+ .then(this.proxy('read_value'));
},
do_update_subscription: function (event) {
@@ -220,8 +229,8 @@ openerp_mail_followers = function(session, mail) {
});
var context = new session.web.CompoundContext(this.build_context(), {});
- return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist:undefined, context])
- .pipe(this.proxy('read_value'));
+ return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist : undefined, context])
+ .then(this.proxy('read_value'));
},
});
};
diff --git a/addons/mail/static/src/js/many2many_tags_email.js b/addons/mail/static/src/js/many2many_tags_email.js
index 52dcbf7aeb5..cc701e8a585 100644
--- a/addons/mail/static/src/js/many2many_tags_email.js
+++ b/addons/mail/static/src/js/many2many_tags_email.js
@@ -46,7 +46,7 @@ instance.web.form.FieldMany2ManyTagsEmail = instance.web.form.FieldMany2ManyTags
["email", "=", false],
["notification_email_send", "in", ['all', 'comment']] ]],
{context: this.build_context()})
- .pipe(function (record_ids) {
+ .then(function (record_ids) {
// valid partner
var valid_partner = _.difference(ids, record_ids);
self.values = self.values.concat(valid_partner);
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index caa85cd3deb..2ba1733043a 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -1,8 +1,9 @@
-
-
+
+
@@ -38,7 +39,8 @@
-
+
+
@@ -69,33 +71,28 @@
Template used to display attachments in a mail.message
-->
-
-
-
-
-
- ...Upload in progress...
-
-
-
-
-
-
-
-
-
- [
-
+
+
+
+
+
[
+
+
+ uploading
+
-
-
-
-
-
-
+
+
+
+
[
+
+
+ uploading
+
+
-
+
@@ -112,7 +109,7 @@
To:
-
Everyone
+
Followers
and
@@ -140,22 +137,20 @@
- News Feed
- /
-
+ Email box
-
@@ -168,7 +163,7 @@
display message on the wall when there are no message
-->
- You have no messages
+ No messages.
-
+
@@ -197,10 +192,11 @@
@@ -208,23 +204,26 @@
-
-
-
@@ -234,11 +233,12 @@
-
+
@@ -247,9 +247,9 @@
mail.compose_message.button_top_bar
render of the button on the user bar for open wizard compose message
-->
-
-
-
+
+
+ %
@@ -257,12 +257,11 @@
Template used to display Like/Unlike in a mail.message
-->
-
8
-
+
like
unlike
diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py
index 8e880a611a7..aea67dcdc13 100644
--- a/addons/mail/tests/test_mail.py
+++ b/addons/mail/tests/test_mail.py
@@ -576,7 +576,7 @@ class test_mail(TestMailMockups):
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)
+ # Data: create a discussion in Pigs (3 threads, with respectively 0, 4 and 4 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')
@@ -588,7 +588,8 @@ class test_mail(TestMailMockups):
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]
+ msg_ids = [msg_id10, msg_id9, msg_id8, msg_id7, msg_id6, msg_id5, msg_id4, msg_id3, msg_id2, msg_id1, msg_id0]
+ ordered_msg_ids = [msg_id2, msg_id4, msg_id6, msg_id8, msg_id10, msg_id1, msg_id3, msg_id5, msg_id7, msg_id9, msg_id0]
# Test: read some specific ids
read_msg_list = self.mail_message.message_read(cr, uid, ids=msg_ids[2:4], domain=[('body', 'like', 'dummy')])
@@ -601,7 +602,8 @@ class test_mail(TestMailMockups):
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')
+ self.assertEqual(ordered_msg_ids, read_msg_ids,
+ 'message_read threaded with domain on Pigs should equal all messages of Pigs, and sort them with newer thread first, last message last in thread')
# ----------------------------------------
# CASE1: message_read with domain, threaded
@@ -611,6 +613,8 @@ class test_mail(TestMailMockups):
# 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']
+ # TDE TODO: test expandables order
+ type_list = map(lambda item: item.get('type'), read_msg_list)
# 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')
@@ -618,7 +622,7 @@ class test_mail(TestMailMockups):
# 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:
+ if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
new_threads_exp = msg
elif msg.get('type') == 'expandable':
new_msg_exp = msg
@@ -630,12 +634,20 @@ class test_mail(TestMailMockups):
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)
+ self.assertEqual(new_msg_exp.get('parent_id'), msg_id2, 'new messages expandable should have parent_id set to the thread header')
+ # Do: message_read with domain, thread_level=0, parent_id=msg_id2 (should be imposed by JS), 2 messages
+ read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=2, 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')
+ new_msg_exp = [msg for msg in read_msg_list if msg.get('type') == 'expandable'][0]
+ # Test: structure content, 2 messages and 1 thread expandable
+ self.assertEqual(len(read_msg_list), 3, 'message_read in Pigs thread should return 2 messages and 1 expandables')
+ self.assertEqual(set([msg_id6, msg_id8]), set(read_msg_ids), 'message_read in Pigs thread should return 2 more previous messages in thread')
+ # Do: read the last message
+ read_msg_list = self.mail_message.message_read(cr, uid, domain=new_msg_exp.get('domain'), limit=2, 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: structure content, 1 message
+ self.assertEqual(len(read_msg_list), 1, 'message_read in Pigs thread should return 1 message')
+ self.assertEqual(set([msg_id4]), set(read_msg_ids), 'message_read in Pigs thread should return the last message in thread')
# 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')
@@ -643,7 +655,7 @@ class test_mail(TestMailMockups):
# 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')
+ self.assertFalse(new_threads_exp.get('parent_id'), 'new threads expandable should not have an parent_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']
@@ -654,7 +666,7 @@ class test_mail(TestMailMockups):
# 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:
+ if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
new_threads_exp = msg
elif msg.get('type') == 'expandable':
new_msg_exp = msg
@@ -701,7 +713,7 @@ class test_mail(TestMailMockups):
# 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:
+ if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
new_threads_exp = msg
# Do: fetch new messages, domain from expandable
@@ -715,7 +727,7 @@ class test_mail(TestMailMockups):
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,
+ self.assertEqual([msg_id8, msg_id7, msg_id6, msg_id5, msg_id4, msg_id3, msg_id2, msg_id1, msg_id0], read_msg_ids,
'message_read, More on flat, should return all remaning messages')
def test_40_needaction(self):
diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py
index 50ecf9450da..1d495fddee7 100644
--- a/addons/mail/wizard/invite.py
+++ b/addons/mail/wizard/invite.py
@@ -33,6 +33,7 @@ class invite_wizard(osv.osv_memory):
result = super(invite_wizard, self).default_get(cr, uid, fields, context=context)
if 'message' in fields and result.get('res_model') and result.get('res_id'):
document_name = self.pool.get(result.get('res_model')).name_get(cr, uid, [result.get('res_id')], context=context)[0][1]
+ print "DOCUMENT_NAME: ",document_name
message = _('You have been invited to follow %s.
' % document_name)
result['message'] = message
elif 'message' in fields:
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 33712847534..45082b9be4d 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -135,7 +135,12 @@ class mail_compose_message(osv.TransientModel):
related to.
:param int res_id: id of the document record this mail is related to
"""
- return {'model': model, 'res_id': res_id}
+ doc_name_get = self.pool.get(model).name_get(cr, uid, res_id, context=context)
+ if doc_name_get:
+ record_name = doc_name_get[0][1]
+ else:
+ record_name = False
+ return {'model': model, 'res_id': res_id, 'record_name': record_name}
def get_message_data(self, cr, uid, message_id, context=None):
""" Returns a defaults-like dict with initial values for the composition
@@ -161,6 +166,7 @@ class mail_compose_message(osv.TransientModel):
# update the result
result = {
+ 'record_name': message_data.record_name,
'model': message_data.model,
'res_id': message_data.res_id,
'parent_id': message_data.id,
diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml
index b9c3d237e78..86b1be6562f 100644
--- a/addons/mail/wizard/mail_compose_message_view.xml
+++ b/addons/mail/wizard/mail_compose_message_view.xml
@@ -14,10 +14,19 @@
-
+
+
+ Followers of
+
+ and
+
+
+
+ attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
diff --git a/addons/marketing_campaign/marketing_campaign_view.xml b/addons/marketing_campaign/marketing_campaign_view.xml
index 189bd8285f2..ab45f802371 100644
--- a/addons/marketing_campaign/marketing_campaign_view.xml
+++ b/addons/marketing_campaign/marketing_campaign_view.xml
@@ -270,7 +270,7 @@
-
+
diff --git a/addons/mrp/i18n/sl.po b/addons/mrp/i18n/sl.po
index 83d80822eb7..c36878f4d1d 100644
--- a/addons/mrp/i18n/sl.po
+++ b/addons/mrp/i18n/sl.po
@@ -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-11-01 19:12+0000\n"
+"PO-Revision-Date: 2012-11-07 23:15+0000\n"
"Last-Translator: Dusan Laznik \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-11-03 05:03+0000\n"
-"X-Generator: Launchpad (build 16218)\n"
+"X-Launchpad-Export-Date: 2012-11-09 04:39+0000\n"
+"X-Generator: Launchpad (build 16250)\n"
#. module: mrp
#: view:mrp.routing.workcenter:0
@@ -34,7 +34,7 @@ msgstr ""
#. module: mrp
#: help:mrp.production,location_src_id:0
msgid "Location where the system will look for components."
-msgstr ""
+msgstr "Lokacija za iskanje komponent"
#. module: mrp
#: field:mrp.production,workcenter_lines:0
@@ -67,11 +67,13 @@ msgid ""
"The 'Minimum stock rule' allows the system to create procurement orders "
"automatically as soon as the minimum stock is reached."
msgstr ""
+"Pravilo \"Minimalna zaloga\" omogoča avtomatsko kreiranje naročil "
+"dobaviteljem."
#. module: mrp
#: field:mrp.production,picking_id:0
msgid "Picking list"
-msgstr ""
+msgstr "Dobavnica"
#. module: mrp
#: code:addons/mrp/report/price.py:130
@@ -83,18 +85,18 @@ msgstr "Urna postavka"
#: code:addons/mrp/report/price.py:139
#, python-format
msgid "Cost Price per Uom"
-msgstr ""
+msgstr "Stroškovna cena na enoto"
#. module: mrp
#: view:mrp.production:0
msgid "Scrap Products"
-msgstr ""
+msgstr "Odpis izdelkov"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.mrp_routing_action
#: model:ir.ui.menu,name:mrp.menu_mrp_routing_action
msgid "Routings"
-msgstr ""
+msgstr "Delovni tok"
#. module: mrp
#: field:mrp.workcenter,product_id:0
@@ -139,7 +141,7 @@ msgstr "Dokončani proizvodi"
#. module: mrp
#: view:mrp.production:0
msgid "Manufacturing Orders which are currently in production."
-msgstr ""
+msgstr "Delovni nalogi trenutno v proizvodnji"
#. module: mrp
#: model:process.transition,name:mrp.process_transition_servicerfq0
@@ -204,6 +206,7 @@ msgid ""
"Fill this product to track easily your production costs in the analytic "
"accounting."
msgstr ""
+"Izpolnite ta izdelek , za spremljavo stroškov v analitičnem računovodstvu"
#. module: mrp
#: model:process.node,note:mrp.process_node_purchaseprocure0
@@ -246,12 +249,12 @@ msgstr "Nabava storitev"
#. module: mrp
#: view:mrp.workcenter:0
msgid "Capacity Information"
-msgstr ""
+msgstr "Informacije o kapacitetah"
#. module: mrp
#: field:mrp.production,move_created_ids2:0
msgid "Produced Products"
-msgstr ""
+msgstr "Končani izdelki"
#. module: mrp
#: report:mrp.production.order:0
@@ -294,7 +297,7 @@ msgstr ""
#. module: mrp
#: help:mrp.bom,position:0
msgid "Reference to a position in an external plan."
-msgstr ""
+msgstr "Referenca na pozicijo v drugem planu"
#. module: mrp
#: constraint:stock.move:0
@@ -309,12 +312,12 @@ msgstr ""
#. module: mrp
#: model:ir.model,name:mrp.model_mrp_product_produce
msgid "Product Produce"
-msgstr ""
+msgstr "Končani izdelki"
#. module: mrp
#: constraint:mrp.bom:0
msgid "Error ! You cannot create recursive BoM."
-msgstr ""
+msgstr "Napaka ! (Rekurzivna kosovnica)"
#. module: mrp
#: model:ir.model,name:mrp.model_mrp_routing_workcenter
@@ -324,7 +327,7 @@ msgstr ""
#. module: mrp
#: model:process.transition,name:mrp.process_transition_procurestockableproduct0
msgid "Procurement of stockable Product"
-msgstr ""
+msgstr "Nabava izdelka , ki se skladišči"
#. module: mrp
#: view:mrp.bom:0
@@ -361,7 +364,7 @@ msgstr "Potrdi Proizvodnjo"
msgid ""
"The system creates an order (production or purchased) depending on the sold "
"quantity and the products parameters."
-msgstr ""
+msgstr "Sistem kreira nalog(delovni ali nabavni), odvisno od parametrov"
#. module: mrp
#: model:process.transition,note:mrp.process_transition_stockproduction0
@@ -386,7 +389,7 @@ msgstr ""
msgid ""
"This is the Internal Picking List that brings the finished product to the "
"production plan"
-msgstr ""
+msgstr "To je interna dobavnica , ki prenese izdelke v plan proizvodnje"
#. module: mrp
#: model:ir.ui.menu,name:mrp.menu_view_resource_calendar_search_mrp
@@ -396,7 +399,7 @@ msgstr "Delovni čas"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.action_report_in_out_picking_tree
msgid "Weekly Stock Value Variation"
-msgstr ""
+msgstr "Tedensko nihanje vrednosti zalog"
#. module: mrp
#: view:mrp.production:0
@@ -426,7 +429,7 @@ msgstr ""
#. module: mrp
#: view:board.board:0
msgid "Stock Value Variation"
-msgstr ""
+msgstr "Nihanje vrednosti zalog"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.action2
@@ -436,7 +439,7 @@ msgstr "Struktura kosovnice"
#. module: mrp
#: model:process.node,note:mrp.process_node_serviceproduct0
msgid "Product type is service"
-msgstr ""
+msgstr "Vrsta izdelka je usluga"
#. module: mrp
#: sql_constraint:res.company:0
@@ -505,12 +508,12 @@ msgstr ""
#: view:mrp.routing:0
#: field:mrp.routing,location_id:0
msgid "Production Location"
-msgstr ""
+msgstr "Lokacija proizvodnje"
#. module: mrp
#: view:mrp.production:0
msgid "Change Qty"
-msgstr ""
+msgstr "Spremeni količino"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.action_configure_workcenter
@@ -520,7 +523,7 @@ msgstr ""
#. module: mrp
#: view:mrp.production:0
msgid "Force Reservation"
-msgstr ""
+msgstr "Vsili rezervacijo"
#. module: mrp
#: field:report.mrp.inout,value:0
@@ -579,7 +582,7 @@ msgstr ""
#. module: mrp
#: field:mrp.workcenter,time_start:0
msgid "Time before prod."
-msgstr ""
+msgstr "Čas pred proizvodnjo"
#. module: mrp
#: help:mrp.routing,active:0
@@ -591,14 +594,14 @@ msgstr ""
#. module: mrp
#: model:process.transition,name:mrp.process_transition_billofmaterialrouting0
msgid "Material Routing"
-msgstr ""
+msgstr "Tok materiala"
#. module: mrp
#: view:mrp.production:0
#: field:mrp.production,move_lines2:0
#: report:mrp.production.order:0
msgid "Consumed Products"
-msgstr ""
+msgstr "Porabljeni izdelki"
#. module: mrp
#: model:ir.actions.act_window,name:mrp.action_mrp_workcenter_load_wizard
@@ -617,7 +620,7 @@ msgstr "Izdelek nima kosovnice"
#: model:ir.actions.act_window,name:mrp.mrp_bom_form_action2
#: model:ir.ui.menu,name:mrp.menu_mrp_bom_form_action2
msgid "Bill of Material Components"
-msgstr ""
+msgstr "Komponente sestavnice"
#. module: mrp
#: model:ir.model,name:mrp.model_stock_move
@@ -651,7 +654,7 @@ msgstr ""
#. module: mrp
#: help:mrp.workcenter,time_cycle:0
msgid "Time in hours for doing one cycle."
-msgstr ""
+msgstr "Čas za končanje cikla"
#. module: mrp
#: constraint:mrp.bom:0
@@ -666,7 +669,7 @@ msgstr "V proizvodnji"
#. module: mrp
#: model:ir.ui.menu,name:mrp.menu_mrp_property
msgid "Master Bill of Materials"
-msgstr ""
+msgstr "Glavna sastavnica"
#. module: mrp
#: help:mrp.bom,product_uos:0
@@ -695,7 +698,7 @@ msgstr ""
#. module: mrp
#: selection:mrp.workcenter.load,time_unit:0
msgid "Per month"
-msgstr ""
+msgstr "Mesečno"
#. module: mrp
#: code:addons/mrp/wizard/change_production_qty.py:78
@@ -707,13 +710,13 @@ msgstr ""
#. module: mrp
#: report:bom.structure:0
msgid "Product Name"
-msgstr ""
+msgstr "Ime Izdelka"
#. module: mrp
#: code:addons/mrp/mrp.py:503
#, python-format
msgid "Invalid action !"
-msgstr ""
+msgstr "Nepravilno dejanje!"
#. module: mrp
#: help:mrp.bom,product_efficiency:0
@@ -725,7 +728,7 @@ msgstr ""
#: code:addons/mrp/mrp.py:762
#, python-format
msgid "Warning!"
-msgstr ""
+msgstr "Opozorilo!"
#. module: mrp
#: report:mrp.production.order:0
@@ -741,17 +744,17 @@ msgstr ""
#. module: mrp
#: model:process.transition,name:mrp.process_transition_producttostockrules0
msgid "Procurement rule"
-msgstr ""
+msgstr "Pravilo nabave"
#. module: mrp
#: view:mrp.production:0
msgid "Partial"
-msgstr ""
+msgstr "Delno"
#. module: mrp
#: report:mrp.production.order:0
msgid "WorkCenter"
-msgstr ""
+msgstr "Proizvodni center"
#. module: mrp
#: model:process.transition,note:mrp.process_transition_procureserviceproduct0
@@ -769,7 +772,7 @@ msgstr "Nujno"
#. module: mrp
#: view:mrp.production:0
msgid "Manufacturing Orders which are waiting for raw materials."
-msgstr ""
+msgstr "Delovni nalogi , ki čakajo na material"
#. module: mrp
#: model:ir.actions.act_window,help:mrp.mrp_workcenter_action
@@ -780,16 +783,18 @@ msgid ""
"resource leave are not taken into account in the time computation of the "
"work center."
msgstr ""
+"Delovno enoto sestavljajo delavci in/ali stroji , ki predstavljajo eno enoto "
+"za planiranje kapacitet."
#. module: mrp
#: model:ir.model,name:mrp.model_mrp_production
msgid "Manufacturing Order"
-msgstr ""
+msgstr "Delovni nalog"
#. module: mrp
#: model:process.transition,name:mrp.process_transition_productionprocureproducts0
msgid "Procurement of raw material"
-msgstr ""
+msgstr "Nabava surovin"
#. module: mrp
#: view:mrp.production:0
@@ -806,7 +811,7 @@ msgstr ""
#: view:mrp.bom:0
#: view:mrp.production:0
msgid "Date"
-msgstr ""
+msgstr "Datum"
#. module: mrp
#: field:mrp.bom,type:0
@@ -823,7 +828,7 @@ msgstr ""
#. module: mrp
#: view:mrp.property:0
msgid "Search"
-msgstr ""
+msgstr "Iskanje"
#. module: mrp
#: code:addons/mrp/mrp.py:626
@@ -884,7 +889,7 @@ msgstr "Možno dati v zalogo"
#: code:addons/mrp/report/price.py:130
#, python-format
msgid "Work Center name"
-msgstr ""
+msgstr "Ime delovne eote"
#. module: mrp
#: field:mrp.routing,code:0
@@ -1034,7 +1039,7 @@ msgstr ""
#. module: mrp
#: help:mrp.workcenter,costs_hour:0
msgid "Specify Cost of Work Center per hour."
-msgstr ""
+msgstr "Cena delovne enote na uro"
#. module: mrp
#: help:mrp.workcenter,capacity_per_cycle:0
@@ -1095,7 +1100,7 @@ msgstr "Analitični dnevnik"
#: model:ir.ui.menu,name:mrp.menu_view_resource_search_mrp
#: field:mrp.routing,workcenter_lines:0
msgid "Work Centers"
-msgstr ""
+msgstr "Delovne enote"
#. module: mrp
#: selection:mrp.workcenter.load,time_unit:0
@@ -1110,6 +1115,8 @@ msgid ""
"They are attached to bills of materials that will define the required raw "
"materials."
msgstr ""
+"Delovni tok opredeljuje zaporedje delovnih enot in operacij za izdelavo "
+"določenega izdelka."
#. module: mrp
#: model:ir.actions.act_window,name:mrp.product_form_config_action
@@ -1152,7 +1159,7 @@ msgstr ""
#. module: mrp
#: view:report.workcenter.load:0
msgid "Work Center load"
-msgstr ""
+msgstr "Zasedenost delovnega centra"
#. module: mrp
#: help:mrp.production,location_dest_id:0
@@ -1853,7 +1860,7 @@ msgstr ""
#. module: mrp
#: field:mrp.routing.workcenter,routing_id:0
msgid "Parent Routing"
-msgstr ""
+msgstr "Nadrejeni delovni tok"
#. module: mrp
#: help:mrp.workcenter,time_start:0
@@ -2013,7 +2020,7 @@ msgstr ""
#: view:mrp.routing:0
#: model:process.node,name:mrp.process_node_routing0
msgid "Routing"
-msgstr "Usmerjanje"
+msgstr "Delovni tok"
#. module: mrp
#: field:mrp.production,date_planned:0
diff --git a/addons/mrp/report/price.py b/addons/mrp/report/price.py
index 527505d8ea5..b1e04749ab2 100644
--- a/addons/mrp/report/price.py
+++ b/addons/mrp/report/price.py
@@ -111,7 +111,7 @@ class report_custom(report_rml):
595.27
841.88
55.00mm,58.00mm,29.00mm,29.00mm,29.00mm
- """ % (user_pool.browse(cr, uid, uid).company_id.name)
+ """ % to_xml(user_pool.browse(cr, uid, uid).company_id.name)
config_stop = """
Generated by OpenERP
diff --git a/addons/mrp/res_config.py b/addons/mrp/res_config.py
index c1712b81a26..ec3deb524a3 100644
--- a/addons/mrp/res_config.py
+++ b/addons/mrp/res_config.py
@@ -28,10 +28,6 @@ class mrp_config_settings(osv.osv_memory):
_inherit = 'res.config.settings'
_columns = {
- 'module_stock_planning': fields.boolean('Manage master production shedule',
- help ="""This allows to create a manual procurement plan apart of the normal MRP scheduling,
- which works automatically based on minimum stock rules.
- This installs the module stock_planning."""),
'module_mrp_repair': fields.boolean("Manage repairs of products ",
help="""Allows to manage all product repairs.
* Add/remove products in the reparation
diff --git a/addons/mrp/res_config_view.xml b/addons/mrp/res_config_view.xml
index 6b4058fb7b4..2c7047380a8 100644
--- a/addons/mrp/res_config_view.xml
+++ b/addons/mrp/res_config_view.xml
@@ -56,10 +56,6 @@
-
-
-
-
diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py
index bc9b9413c65..d79620ce93c 100644
--- a/addons/mrp_repair/mrp_repair.py
+++ b/addons/mrp_repair/mrp_repair.py
@@ -690,7 +690,7 @@ class mrp_repair_line(osv.osv, ProductChangeMixin):
'repair_id': fields.many2one('mrp.repair', 'Repair Order Reference',ondelete='cascade', select=True),
'type': fields.selection([('add','Add'),('remove','Remove')],'Type', required=True),
'to_invoice': fields.boolean('To Invoice'),
- 'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok','=',True)], required=True),
+ 'product_id': fields.many2one('product.product', 'Product', required=True),
'invoiced': fields.boolean('Invoiced',readonly=True),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
'price_subtotal': fields.function(_amount_line, string='Subtotal',digits_compute= dp.get_precision('Account')),
diff --git a/addons/note/note_view.xml b/addons/note/note_view.xml
index 18a3ec840a9..5aae6204d86 100644
--- a/addons/note/note_view.xml
+++ b/addons/note/note_view.xml
@@ -120,7 +120,7 @@
-
+
diff --git a/addons/note/static/src/css/note.css b/addons/note/static/src/css/note.css
index 1b7e9315e74..16d71e28aa5 100644
--- a/addons/note/static/src/css/note.css
+++ b/addons/note/static/src/css/note.css
@@ -49,8 +49,8 @@
padding: 8px;
margin-left: 3px;
margin-right: 3px;
- padding-bottom: 16px;
- margin-bottom: 16px;
+ padding-bottom: 8px;
+ margin-bottom: 10px;
-webkit-transform: rotate(-2deg);
-o-transform: rotate(-2deg);
-moz-transform: rotate(-2deg);
diff --git a/addons/note/static/src/css/note.sass b/addons/note/static/src/css/note.sass
index b5bc28a2593..6f0c0883ae7 100644
--- a/addons/note/static/src/css/note.sass
+++ b/addons/note/static/src/css/note.sass
@@ -53,8 +53,8 @@
padding: 8px
margin-left: 3px
margin-right: 3px
- padding-bottom: 16px
- margin-bottom: 16px
+ padding-bottom: 8px
+ margin-bottom: 10px
@include rotate(-2deg)
@include transition(all, 300ms)
.oe_kanban_record:nth-of-type(even)
diff --git a/addons/pad/pad.py b/addons/pad/pad.py
index e5b36e5f64c..932fc5a21a4 100644
--- a/addons/pad/pad.py
+++ b/addons/pad/pad.py
@@ -19,7 +19,7 @@ class pad_common(osv.osv_memory):
pad = {
"server" : company.pad_server,
- "key" : company.pad_key or "4DxmsNIbnQUVQMW9S9tx2oLOSjFdrx1l",
+ "key" : company.pad_key,
}
# make sure pad server in the form of http://hostname
diff --git a/addons/pad/pad_demo.xml b/addons/pad/pad_demo.xml
index 407f90e8242..99c4b38481c 100644
--- a/addons/pad/pad_demo.xml
+++ b/addons/pad/pad_demo.xml
@@ -1,9 +1,13 @@
-
+
pad.openerp.com
+
+
+ 4DxmsNIbnQUVQMW9S9tx2oLOSjFdrx1l
+
diff --git a/addons/pad/static/src/js/pad.js b/addons/pad/static/src/js/pad.js
index c1252642b91..929d62681c8 100644
--- a/addons/pad/static/src/js/pad.js
+++ b/addons/pad/static/src/js/pad.js
@@ -12,8 +12,9 @@ openerp.pad = function(instance) {
model: self.view.model,
field_name: self.name,
object_id: self.view.datarecord.id
- }}).then(function(data) {
+ }}).done(function(data) {
if(data&&data.url){
+ self.set({value: data.url});
_super(data.url);
self.renderElement();
}
diff --git a/addons/plugin_outlook/plugin_outlook.xml b/addons/plugin_outlook/plugin_outlook.xml
index e99fbf72b6d..91c51e87ad9 100644
--- a/addons/plugin_outlook/plugin_outlook.xml
+++ b/addons/plugin_outlook/plugin_outlook.xml
@@ -10,7 +10,11 @@
Install Outlook Plug-In
-
+
+
+
@@ -19,10 +23,10 @@
Click on the link above to download the installer for either 32 or 64 bits, and execute it.
System requirements:
-
- 1. MS Outlook 2005 or above.
- 2. MS .Net Framework 3.5 or above.
-
+
+ MS Outlook 2005 or above.
+ MS .Net Framework 3.5 or above.
+
@@ -51,7 +55,7 @@
-
diff --git a/addons/plugin_thunderbird/plugin_thunderbird.py b/addons/plugin_thunderbird/plugin_thunderbird.py
index 6473758883d..923611b97a9 100644
--- a/addons/plugin_thunderbird/plugin_thunderbird.py
+++ b/addons/plugin_thunderbird/plugin_thunderbird.py
@@ -30,13 +30,11 @@ class plugin_thunderbird_installer(osv.osv_memory):
'thunderbird': fields.boolean('Thunderbird Plug-in', help="Allows you to select an object that you would like to add to your email and its attachments."),
'plugin_name': fields.char('File name', size=64),
'plugin_file': fields.char('Thunderbird Plug-in', size=256, readonly=True, help="Thunderbird plug-in file. Save this file and install it in Thunderbird."),
- 'pdf_file': fields.char('Installation Manual', size=256, help="The documentation file :- how to install Thunderbird Plug-in.", readonly=True),
}
_defaults = {
'thunderbird': True,
'plugin_name': 'openerp_plugin.xpi',
- 'pdf_file': 'http://doc.openerp.com/book/2/2_6_Comms/2_6_Comms_thunderbird.html',
}
def default_get(self, cr, uid, fields, context=None):
@@ -45,4 +43,4 @@ class plugin_thunderbird_installer(osv.osv_memory):
res['plugin_file'] = base_url + '/plugin_thunderbird/static/openerp_plugin.xpi'
return res
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/plugin_thunderbird/plugin_thunderbird.xml b/addons/plugin_thunderbird/plugin_thunderbird.xml
index 5ea5fc38219..3774a369437 100644
--- a/addons/plugin_thunderbird/plugin_thunderbird.xml
+++ b/addons/plugin_thunderbird/plugin_thunderbird.xml
@@ -10,25 +10,28 @@
Install Thunderbird Plug-In
-
+
+
+
-
- Thunderbird plugin installation:
-
- 1. Save the Thunderbird plug-in.
- 2. From the Thunderbird menubar: Tools > Add-ons -> Screwdriver/Wrench Icon -> Install add-on from file...
- 3. Select the plug-in (the file named openerp_plugin.xpi).
- 4. Click "Install Now".
- 5. Restart Thunderbird.
- 6. From the Thunderbird menubar: OpenERP -> Configuration.
- 7. Configure your openerp server.
-
+ Thunderbird plug-in installation:
+
+ Save the Thunderbird plug-in.
+ From the Thunderbird menubar: Tools > Add-ons -> Screwdriver/Wrench Icon -> Install add-on from file...
+ Select the plug-in (the file named openerp_plugin.xpi).
+ Click "Install Now".
+ Restart Thunderbird.
+ From the Thunderbird menubar: OpenERP -> Configuration.
+ Configure your openerp server.
+
@@ -57,7 +60,7 @@
-
diff --git a/addons/point_of_sale/report/pos_receipt.py b/addons/point_of_sale/report/pos_receipt.py
index 2ee6b2e3360..58044c28c77 100644
--- a/addons/point_of_sale/report/pos_receipt.py
+++ b/addons/point_of_sale/report/pos_receipt.py
@@ -34,7 +34,7 @@ class order(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context):
super(order, self).__init__(cr, uid, name, context=context)
- user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid)
+ user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid, context=context)
partner = user.company_id.partner_id
self.localcontext.update({
diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js
index c96aff3d9c1..c5580ec8e46 100644
--- a/addons/point_of_sale/static/src/js/devices.js
+++ b/addons/point_of_sale/static/src/js/devices.js
@@ -37,12 +37,11 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
callbacks[i](params);
}
- this.connection.rpc('/pos/'+name, params || {}).then(function(result){
- ret.resolve(result);
- },
- function(error){
- ret.reject(error);
- });
+ this.connection.rpc('/pos/' + name, params || {}).done(function(result) {
+ ret.resolve(result);
+ }).fail(function(error) {
+ ret.reject(error);
+ });
return ret;
},
@@ -96,7 +95,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
return this.weight;
}else{
this.message('weighting_read_kg',{})
- .then(function(weight){
+ .done(function(weight){
if(self.weighting && !self.bypass_proxy){
self.weight = weight;
}
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index d7d6d3181f1..7301a85eb2d 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -59,10 +59,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched.
// when all the data has loaded, we compute some stuff, and declare the Pos ready to be used.
$.when(this.load_server_data())
- .then(function(){
+ .done(function(){
//self.log_loaded_data(); //Uncomment if you want to log the data to the console for easier debugging
self.ready.resolve();
- },function(){
+ }).fail(function(){
//we failed to load some backend data, or the backend was badly configured.
//the error messages will be displayed in PosWidget
self.ready.reject();
@@ -78,7 +78,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
var self = this;
var loaded = self.fetch('res.users',['name','company_id'],[['id','=',this.session.uid]])
- .pipe(function(users){
+ .then(function(users){
self.set('user',users[0]);
return self.fetch('res.company',
@@ -93,19 +93,19 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
'partner_id',
],
[['id','=',users[0].company_id[0]]]);
- }).pipe(function(companies){
+ }).then(function(companies){
self.set('company',companies[0]);
return self.fetch('res.partner',['contact_address'],[['id','=',companies[0].partner_id[0]]]);
- }).pipe(function(company_partners){
+ }).then(function(company_partners){
self.get('company').contact_address = company_partners[0].contact_address;
return self.fetch('res.currency',['symbol','position'],[['id','=',self.get('company').currency_id[0]]]);
- }).pipe(function(currencies){
+ }).then(function(currencies){
self.set('currency',currencies[0]);
return self.fetch('product.uom', null, null);
- }).pipe(function(units){
+ }).then(function(units){
self.set('units',units);
var units_by_id = {};
for(var i = 0, len = units.length; i < len; i++){
@@ -114,15 +114,15 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
self.set('units_by_id',units_by_id);
return self.fetch('product.packaging', null, null);
- }).pipe(function(packagings){
+ }).then(function(packagings){
self.set('product.packaging',packagings);
return self.fetch('res.users', ['name','ean13'], [['ean13', '!=', false]]);
- }).pipe(function(users){
+ }).then(function(users){
self.set('user_list',users);
return self.fetch('account.tax', ['amount', 'price_include', 'type']);
- }).pipe(function(taxes){
+ }).then(function(taxes){
self.set('taxes', taxes);
return self.fetch(
@@ -130,7 +130,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
['id', 'journal_ids','name','user_id','config_id','start_at','stop_at'],
[['state', '=', 'opened'], ['user_id', '=', self.session.uid]]
);
- }).pipe(function(sessions){
+ }).then(function(sessions){
self.set('pos_session', sessions[0]);
return self.fetch(
@@ -141,7 +141,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
'iface_print_via_proxy','iface_cashdrawer','state','sequence_id','session_ids'],
[['id','=', self.get('pos_session').config_id[0]]]
);
- }).pipe(function(configs){
+ }).then(function(configs){
var pos_config = configs[0];
self.set('pos_config', pos_config);
self.iface_electronic_scale = !!pos_config.iface_electronic_scale;
@@ -151,15 +151,15 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
self.iface_cashdrawer = !!pos_config.iface_cashdrawer;
return self.fetch('sale.shop',[],[['id','=',pos_config.shop_id[0]]]);
- }).pipe(function(shops){
+ }).then(function(shops){
self.set('shop',shops[0]);
return self.fetch('product.packaging',['ean','product_id']);
- }).pipe(function(packagings){
+ }).then(function(packagings){
self.db.add_packagings(packagings);
return self.fetch('pos.category', ['id','name','parent_id','child_id','image'])
- }).pipe(function(categories){
+ }).then(function(categories){
self.db.add_categories(categories);
return self.fetch(
@@ -169,7 +169,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
[['pos_categ_id','!=', false],['sale_ok','=',true]],
{pricelist: self.get('shop').pricelist_id[0]} // context for price
);
- }).pipe(function(products){
+ }).then(function(products){
self.db.add_products(products);
return self.fetch(
@@ -177,14 +177,14 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
['account_id','currency','journal_id','state','name','user_id','pos_session_id'],
[['state','=','open'],['pos_session_id', '=', self.get('pos_session').id]]
);
- }).pipe(function(bank_statements){
+ }).then(function(bank_statements){
var journals = new Array();
_.each(bank_statements,function(statement) {
journals.push(statement.journal_id[0])
});
self.set('bank_statements', bank_statements);
return self.fetch('account.journal', undefined, [['id','in', journals]]);
- }).pipe(function(journals){
+ }).then(function(journals){
self.set('journals',journals);
// associate the bank statements with their journals.
diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js
index 8028349b177..0b8ab70a9c0 100644
--- a/addons/point_of_sale/static/src/js/screens.js
+++ b/addons/point_of_sale/static/src/js/screens.js
@@ -557,7 +557,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
setTimeout(function(){
var def = job.fun();
if(def){
- def.then(run);
+ def.done(run);
}else{
run();
}
@@ -615,7 +615,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
var def = new $.Deferred();
console.log("START");
self.pos.proxy.payment_request(self.pos.get('selectedOrder').getDueLeft())
- .then(function(ack){
+ .done(function(ack){
if(ack === 'ok'){
self.queue.schedule(self.update);
}else if(ack.indexOf('error') === 0){
@@ -638,7 +638,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
return def.resolve();
}
self.pos.proxy.payment_status()
- .then(function(status){
+ .done(function(status){
if(status.status === 'paid'){
var currentOrder = self.pos.get('selectedOrder');
@@ -941,8 +941,10 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
var self = this;
var x = new module.PaymentlineWidget(null, {
payment_line: newPaymentLine
- });
- x.on('delete_payment_line', self, self.deleteLine);
+ });
+ x.on('delete_payment_line', self, function(r) {
+ self.deleteLine(r);
+ });
x.appendTo(this.$('#paymentlines'));
},
renderElement: function() {
diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js
index ed9b1fd060a..c7874a4bd5c 100644
--- a/addons/point_of_sale/static/src/js/widgets.js
+++ b/addons/point_of_sale/static/src/js/widgets.js
@@ -315,7 +315,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this._super();
this.$('input').keyup(_.bind(this.changeAmount, this));
this.$('.delete-payment-line').click(function() {
- self.trigger('delete_payment_line');
+ self.trigger('delete_payment_line', self);
});
},
});
@@ -806,7 +806,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
start: function() {
var self = this;
- return self.pos.ready.then(function() {
+ return self.pos.ready.done(function() {
self.build_currency_template();
self.renderElement();
@@ -845,7 +845,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.$('.loader').animate({opacity:0},1500,'swing',function(){self.$('.loader').hide();});
self.$('.loader img').hide();
- },function(){ // error when loading models data from the backend
+ }).fail(function(){ // error when loading models data from the backend
self.$('.loader img').hide();
return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id'])
.pipe( _.bind(function(res){
@@ -1051,7 +1051,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.pos.barcode_reader.disconnect();
return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']).pipe(
_.bind(function(res) {
- return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(action) {
+ return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(result) {
+ var action = result;
action.context = _.extend(action.context || {}, {'cancel_action': {type: 'ir.actions.client', tag: 'reload'}});
//self.destroy();
this.do_action(action);
diff --git a/addons/point_of_sale/wizard/pos_payment.py b/addons/point_of_sale/wizard/pos_payment.py
index 9511aed6331..70b88f35e33 100644
--- a/addons/point_of_sale/wizard/pos_payment.py
+++ b/addons/point_of_sale/wizard/pos_payment.py
@@ -75,7 +75,7 @@ class pos_make_payment(osv.osv_memory):
def launch_payment(self, cr, uid, ids, context=None):
return {
- 'name': _('Paiement'),
+ 'name': _('Payment'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'pos.make.payment',
@@ -83,6 +83,7 @@ class pos_make_payment(osv.osv_memory):
'target': 'new',
'views': False,
'type': 'ir.actions.act_window',
+ 'context': context,
}
def print_report(self, cr, uid, ids, context=None):
diff --git a/addons/portal/wizard/share_wizard.py b/addons/portal/wizard/share_wizard.py
index b1b6e980dec..51e5e59eb0e 100644
--- a/addons/portal/wizard/share_wizard.py
+++ b/addons/portal/wizard/share_wizard.py
@@ -35,8 +35,8 @@ class share_wizard_portal(osv.TransientModel):
def _user_type_selection(self, cr, uid, context=None):
selection = super(share_wizard_portal, self)._user_type_selection(cr, uid, context=context)
- selection.extend([('existing','Users you already shared with'),
- ('groups','Existing Groups (e.g Portal Groups)')])
+ selection.extend([('existing',_('Users you already shared with')),
+ ('groups',_('Existing Groups (e.g Portal Groups)'))])
return selection
_columns = {
diff --git a/addons/portal_claim/portal_claim_view.xml b/addons/portal_claim/portal_claim_view.xml
index 86be0764d26..e08b39e476b 100644
--- a/addons/portal_claim/portal_claim_view.xml
+++ b/addons/portal_claim/portal_claim_view.xml
@@ -10,7 +10,14 @@
{"search_default_user_id":'', "stage_type":'claim'}
-
Record and track your customers' claims. Claims may be linked to a sales order or a lot. You can send emails with attachments and keep the full history for a claim (emails sent, intervention type and so on). Claims may automatically be linked to an email address using the mail gateway module.
+
+
+ Click to register a new claim.
+
+ You can track your claims from this menu and the action we
+ will take.
+
+
kanban,calendar,tree,form,graph
{"search_default_upcoming":1}
- No public events.
+ There are no public events.
kanban,tree,form
- No public products.
+ There are no public products.
@@ -65,7 +65,7 @@
{'type':'receipt'}
current
- You don't have any payment.
+ You don't have any refunds or payments.
Portal Personal Contacts
[('message_follower_ids','in',[user.partner_id.id])]
+
diff --git a/addons/process/static/src/js/process.js b/addons/process/static/src/js/process.js
index 2405f6d2aa5..73ca4396ce6 100644
--- a/addons/process/static/src/js/process.js
+++ b/addons/process/static/src/js/process.js
@@ -20,7 +20,7 @@ instance.web.ViewManager.include({
if(this.active_view == 'form') {
this.record_id = this.views[this.active_view].controller.datarecord.id;
}
- this.process_get_object().pipe(function(process) {
+ this.process_get_object().then(function(process) {
if(process && process.length) {
if(process.length > 1) {
self.process_selection = process;
@@ -30,7 +30,7 @@ instance.web.ViewManager.include({
}
}
return $.Deferred().resolve();
- }).pipe(function() {
+ }).then(function() {
var def = $.Deferred();
if(self.process_id) {
$.when(self.process_graph_get()).done(function(res) {
@@ -221,12 +221,12 @@ instance.web.ViewManager.include({
var dataset = new instance.web.DataSet(this, 'ir.values', this.session.user_context);
var action_manager = new instance.web.ActionManager(self);
dataset.call('get',
- ['action', 'tree_but_open',[['ir.ui.menu', id]], dataset.context]).then(function(res) {
+ ['action', 'tree_but_open',[['ir.ui.menu', id]], dataset.context]).done(function(res) {
var action = res[0][res[0].length - 1];
self.rpc("/web/action/load", {
action_id: action.id,
context: dataset.context
- }).then(function(result) {
+ }).done(function(result) {
action_manager.replace(self.$el);
action_manager.do_action(result.result);
})
diff --git a/addons/product/product.py b/addons/product/product.py
index b6a58fcd1e8..27cd4249a60 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -657,9 +657,9 @@ class product_product(osv.osv):
# Performing a quick memory merge of ids in Python will give much better performance
ids = set()
ids.update(self.search(cr, user, args + [('default_code',operator,name)], limit=limit, context=context))
- if len(ids) < limit:
+ if not limit or len(ids) < limit:
# we may underrun the limit because of dupes in the results, that's fine
- ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit-len(ids)), context=context))
+ ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit and (limit-len(ids)) or False) , context=context))
ids = list(ids)
if not ids:
ptrn = re.compile('(\[(.*?)\])')
diff --git a/addons/product_expiry/product_expiry.py b/addons/product_expiry/product_expiry.py
index c16ccc2b1ee..e4ed28db4e6 100644
--- a/addons/product_expiry/product_expiry.py
+++ b/addons/product_expiry/product_expiry.py
@@ -45,12 +45,13 @@ class stock_production_lot(osv.osv):
_columns = {
'life_date': fields.datetime('End of Life Date',
- help='The date on which the lot may become dangerous and should not be consumed.'),
+ help='This is the date on which the goods with this Serial Number may become dangerous and must not be consumed.'),
'use_date': fields.datetime('Best before Date',
- help='The date on which the lot starts deteriorating without becoming dangerous.'),
+ help='This is the date on which the goods with this Serial Number start deteriorating, without being dangerous yet.'),
'removal_date': fields.datetime('Removal Date',
- help='The date on which the lot should be removed.'),
- 'alert_date': fields.datetime('Alert Date', help="The date on which an alert should be notified about the serial number."),
+ help='This is the date on which the goods with this Serial Number should be removed from the stock.'),
+ 'alert_date': fields.datetime('Alert Date',
+ help="This is the date on which an alert should be notified about the goods with this Serial Number."),
}
# Assign dates according to products data
def create(self, cr, uid, vals, context=None):
@@ -78,12 +79,13 @@ class product_product(osv.osv):
_inherit = 'product.product'
_columns = {
'life_time': fields.integer('Product Life Time',
- help='The number of days before a serial number may become dangerous and should not be consumed.'),
+ help='When a new a Serial Number is issued, this is the number of days before the goods may become dangerous and must not be consumed.'),
'use_time': fields.integer('Product Use Time',
- help='The number of days before a serial number starts deteriorating without becoming dangerous.'),
+ help='When a new a Serial Number is issued, this is the number of days before the goods starts deteriorating, without being dangerous yet.'),
'removal_time': fields.integer('Product Removal Time',
- help='The number of days before a serial number should be removed.'),
- 'alert_time': fields.integer('Product Alert Time', help="The number of days after which an alert should be notified about the serial number."),
+ help='When a new a Serial Number is issued, this is the number of days before the goods should be removed from the stock.'),
+ 'alert_time': fields.integer('Product Alert Time',
+ help='When a new a Serial Number is issued, this is the number of days before an alert should be notified.'),
}
product_product()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project/project.py b/addons/project/project.py
index 2811f638f73..9c28fadeab9 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -166,17 +166,17 @@ class project(osv.osv):
res[id]['progress_rate'] = 0.0
return res
- def unlink(self, cr, uid, ids, *args, **kwargs):
+ def unlink(self, cr, uid, ids, context=None):
alias_ids = []
mail_alias = self.pool.get('mail.alias')
- for proj in self.browse(cr, uid, ids):
+ for proj in self.browse(cr, uid, ids, context=context):
if proj.tasks:
raise osv.except_osv(_('Invalid Action!'),
_('You cannot delete a project containing tasks. You can either delete all the project\'s tasks and then delete the project or simply deactivate the project.'))
elif proj.alias_id:
alias_ids.append(proj.alias_id.id)
- res = super(project, self).unlink(cr, uid, ids, *args, **kwargs)
- mail_alias.unlink(cr, uid, alias_ids, *args, **kwargs)
+ res = super(project, self).unlink(cr, uid, ids, context=context)
+ mail_alias.unlink(cr, uid, alias_ids, context=context)
return res
def _get_attached_docs(self, cr, uid, ids, field_name, arg, context):
@@ -203,7 +203,11 @@ class project(osv.osv):
def attachment_tree_view(self, cr, uid, ids, context):
task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)])
- domain = ['|', '&', ('res_model', '=', 'project.project'), ('res_id', 'in', ids), '&', ('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)]
+ domain = [
+ '|',
+ '&', ('res_model', '=', 'project.project'), ('res_id', 'in', ids),
+ '&', ('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)
+ ]
res_id = ids and ids[0] or False
return {
'name': _('Attachments'),
@@ -470,7 +474,7 @@ def Project():
resource = %s
""" % (
project.id,
- project.date_start, working_days,
+ project.date_start or time.strftime('%Y-%m-%d'), working_days,
'|'.join(['User_'+str(x) for x in puids])
)
vacation = calendar_id and tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context)) or False
@@ -1301,7 +1305,17 @@ class task(base_stage, osv.osv):
msg = _('Task has been delegated to %s .') % (task.user_id.name)
self.message_post(cr, uid, [task.id], body=msg, context=context)
return True
-
+
+ def project_task_reevaluate(self, cr, uid, ids, context=None):
+ if self.pool.get('res.users').has_group(cr, uid, 'project.group_time_work_estimation_tasks'):
+ return {
+ 'view_type': 'form',
+ "view_mode": 'form',
+ 'res_model': 'project.task.reevaluate',
+ 'type': 'ir.actions.act_window',
+ 'target': 'new',
+ }
+ return self.do_reopen(cr, uid, ids, context=context)
class project_work(osv.osv):
_name = "project.task.work"
diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml
index ec29b19e97b..e7f12ff3797 100644
--- a/addons/project/project_view.xml
+++ b/addons/project/project_view.xml
@@ -164,7 +164,7 @@
project.project
-
+
@@ -392,7 +392,7 @@
-->
-
diff --git a/addons/project/static/src/js/project.js b/addons/project/static/src/js/project.js
index 59eeba4d191..38578a6226e 100644
--- a/addons/project/static/src/js/project.js
+++ b/addons/project/static/src/js/project.js
@@ -16,7 +16,7 @@ openerp.project = function(openerp) {
// Find their matching names
var dataset = new openerp.web.DataSetSearch(self, 'res.users', self.session.context, [['id', 'in', _.uniq(members_ids)]]);
- dataset.read_slice(['id', 'name']).then(function(result) {
+ dataset.read_slice(['id', 'name']).done(function(result) {
_.each(result, function(v, k) {
// Set the proper value in the DOM
self.$el.find('img[data-member_id=' + v.id + ']').attr('title', v.name).tipsy({
@@ -41,7 +41,7 @@ openerp.project = function(openerp) {
// Find their matching names
var dataset = new openerp.web.DataSetSearch(self, 'project.category', self.session.context, [['id', 'in', _.uniq(categ_ids)]]);
- dataset.read_slice(['id', 'name']).then(function(result) {
+ dataset.read_slice(['id', 'name']).done(function(result) {
_.each(result, function(v, k) {
// Set the proper value in the DOM and display the element
self.$el.find('span[data-categ_id=' + v.id + ']').text(v.name);
diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py
index 3f85e5c2e79..439ff6d835d 100644
--- a/addons/project_timesheet/project_timesheet.py
+++ b/addons/project_timesheet/project_timesheet.py
@@ -199,8 +199,8 @@ class project_work(osv.osv):
if amount_unit and 'amount' in amount_unit.get('value',{}):
vals_line['amount'] = amount_unit['value']['amount']
- if line_id.sheet_id.state in ['confirm' 'done']:
- self.pool.get('hr.analytic.timesheet').write(cr, uid, [line_id.id], vals_line, context=context)
+
+ self.pool.get('hr.analytic.timesheet').write(cr, uid, [line_id.id], vals_line, context=context)
return super(project_work,self).write(cr, uid, ids, vals, context)
@@ -241,6 +241,7 @@ class task(osv.osv):
if vals.get('project_id',False):
project_obj = self.pool.get('project.project').browse(cr, uid, vals['project_id'], context=context)
acc_id = project_obj.analytic_account_id.id
+
for task_obj in self.browse(cr, uid, ids, context=context):
if len(task_obj.work_ids):
for task_work in task_obj.work_ids:
diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml
index daad4467929..fab7eeacf6c 100644
--- a/addons/purchase/edi/purchase_order_action_data.xml
+++ b/addons/purchase/edi/purchase_order_action_data.xml
@@ -40,7 +40,7 @@
Automated Purchase Order Notification Mail
${object.validator.email or ''}
${object.company_id.name} Order (Ref ${object.name or 'n/a' })
- ${object.partner_id.email}
+ ${object.partner_id.id}
1:
+ result['domain'] = "[('id','in',["+','.join(map(str, inv_ids))+"])]"
+ else:
+ res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_supplier_form')
+ result['views'] = [(res and res[1] or False, 'form')]
+ result['res_id'] = inv_ids and inv_ids[0] or False
+ return result
+
def view_invoice(self, cr, uid, ids, context=None):
'''
This function returns an action that display existing invoices of given sale order ids. It can either be a in a list or in a form view, if there is only one invoice to show.
@@ -374,6 +395,7 @@ class purchase_order(osv.osv):
'default_res_id': ids[0],
'default_use_template': True,
'default_template_id': template_id,
+ 'default_composition_mode': 'comment',
})
return {
'view_type': 'form',
@@ -547,7 +569,6 @@ class purchase_order(osv.osv):
'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none',
'type': 'in',
'partner_id': order.dest_address_id.id or order.partner_id.id,
- 'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none',
'purchase_id': order.id,
'company_id': order.company_id.id,
'move_lines' : [],
@@ -803,7 +824,7 @@ class purchase_order_line(osv.osv):
cur_obj=self.pool.get('res.currency')
tax_obj = self.pool.get('account.tax')
for line in self.browse(cr, uid, ids, context=context):
- taxes = tax_obj.compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty)
+ taxes = tax_obj.compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id, line.order_id.partner_id)
cur = line.order_id.pricelist_id.currency_id
res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
return res
diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml
index 7e94a0b5a8e..db71a969fbe 100644
--- a/addons/purchase/purchase_view.xml
+++ b/addons/purchase/purchase_view.xml
@@ -268,7 +268,7 @@
-
+
@@ -282,8 +282,6 @@
-
-
diff --git a/addons/purchase/res_config.py b/addons/purchase/res_config.py
index 19b059d4d5b..9e781d661f2 100644
--- a/addons/purchase/res_config.py
+++ b/addons/purchase/res_config.py
@@ -43,14 +43,13 @@ class purchase_config_settings(osv.osv_memory):
'group_costing_method':fields.boolean("Compute product cost price based on average cost",
implied_group='product.group_costing_method',
help="""Allows you to compute product cost price based on average cost."""),
- 'group_purchase_delivery_address': fields.boolean("Allow a different address for incoming products and invoicings",
+ 'group_purchase_delivery_address': fields.boolean("Allow a different address for incoming products and invoicing",
implied_group='purchase.group_delivery_invoice_address',
help="Allows you to specify different delivery and invoice addresses on a purchase order."),
'module_warning': fields.boolean("Alerts by products or supplier",
- help="""Allow to configure warnings on products and trigger them when a user wants to purchase a given product or a given supplier.
- Example: Product: this product is deprecated, do not purchase more than 5.
- Supplier: don't forget to ask for an express delivery."""),
-
+ help="""Allow to configure notification on products and trigger them when a user wants to purchase a given product or a given supplier.
+Example: Product: this product is deprecated, do not purchase more than 5.
+ Supplier: don't forget to ask for an express delivery."""),
'module_purchase_double_validation': fields.boolean("Force two levels of approvals",
help="""Provide a double validation mechanism for purchases exceeding minimum amount.
This installs the module purchase_double_validation."""),
diff --git a/addons/purchase/stock_view.xml b/addons/purchase/stock_view.xml
index 9e5aa2e074e..188baeccd69 100644
--- a/addons/purchase/stock_view.xml
+++ b/addons/purchase/stock_view.xml
@@ -48,6 +48,8 @@
+
diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml
index 9796d0bda16..da1a1ceb393 100644
--- a/addons/sale/edi/sale_order_action_data.xml
+++ b/addons/sale/edi/sale_order_action_data.xml
@@ -25,7 +25,7 @@
Automated Sale Order Notification Mail
${object.user_id.email or ''}
${object.company_id.name} Order (Ref ${object.name or 'n/a' })
-
${object.partner_invoice_id.email}
+
${object.partner_invoice_id.id}
-
-
+
+
+
diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py
index 4e6d6414ddf..9ea2215fe0f 100644
--- a/addons/sale/wizard/sale_make_invoice_advance.py
+++ b/addons/sale/wizard/sale_make_invoice_advance.py
@@ -123,6 +123,7 @@ class sale_advance_payment_inv(osv.osv_memory):
# create the invoice
inv_line_values = {
'name': res.get('name'),
+ 'origin': sale.name,
'account_id': res['account_id'],
'price_unit': inv_amount,
'quantity': wizard.qtty or 1.0,
diff --git a/addons/sale_crm/wizard/crm_make_sale.py b/addons/sale_crm/wizard/crm_make_sale.py
index 2597c1db56c..10225990229 100644
--- a/addons/sale_crm/wizard/crm_make_sale.py
+++ b/addons/sale_crm/wizard/crm_make_sale.py
@@ -64,7 +64,9 @@ class crm_make_sale(osv.osv_memory):
"""
if context is None:
context = {}
-
+ # update context: if come from phonecall, default state values can make the quote crash lp:1017353
+ context.pop('default_state', False)
+
case_obj = self.pool.get('crm.lead')
sale_obj = self.pool.get('sale.order')
partner_obj = self.pool.get('res.partner')
diff --git a/addons/share/static/src/js/share.js b/addons/share/static/src/js/share.js
index e73e68144d0..e627b555de3 100644
--- a/addons/share/static/src/js/share.js
+++ b/addons/share/static/src/js/share.js
@@ -15,7 +15,7 @@ openerp.share = function(session) {
self.rpc('/web/session/eval_domain_and_context', {
domains: [domain],
contexts: [view.dataset.context]
- }).then(function (result) {
+ }).done(function (result) {
Share.create({
name: action.name,
record_name: rec_name,
@@ -24,8 +24,8 @@ openerp.share = function(session) {
user_type: user_type || 'embedded',
view_type: view.fields_view.type,
invite: invite || false,
- }).then(function(share_id) {
- var step1 = Share.call('go_step_1', [[share_id]]).then(function(result) {
+ }).done(function(share_id) {
+ var step1 = Share.call('go_step_1', [[share_id]]).done(function(result) {
var action = result;
self.do_action(action);
});
@@ -37,7 +37,7 @@ openerp.share = function(session) {
if (!session.session.share_flag) {
session.session.share_flag = $.Deferred(function() {
var func = new session.web.Model("share.wizard").get_func("has_share");
- func(session.session.uid).pipe(function(res) {
+ func(session.session.uid).then(function(res) {
if(res) {
session.session.share_flag.resolve();
} else {
diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py
index 360b1eff159..6a1f5681f4f 100644
--- a/addons/share/wizard/share_wizard.py
+++ b/addons/share/wizard/share_wizard.py
@@ -74,7 +74,7 @@ class share_wizard(osv.TransientModel):
def _user_type_selection(self, cr, uid, context=None):
"""Selection values may be easily overridden/extended via inheritance"""
- return [('embedded', 'Direct link or embed code'), ('emails','Emails'), ]
+ return [('embedded', _('Direct link or embed code')), ('emails',_('Emails')), ]
"""Override of create() to auto-compute the action name"""
def create(self, cr, uid, values, context=None):
diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml
index 250fe5245e7..594843eff8d 100644
--- a/addons/stock/product_view.xml
+++ b/addons/stock/product_view.xml
@@ -41,7 +41,7 @@
-
+
@@ -103,7 +103,7 @@
-
+
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 606cb9247d5..d18a55ea49a 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -1351,14 +1351,14 @@ class stock_picking(osv.osv):
wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
# Then we finish the good picking
self.write(cr, uid, [pick.id], {'backorder_id': new_picking})
- self.action_move(cr, uid, [new_picking])
+ self.action_move(cr, uid, [new_picking], context=context)
wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_done', cr)
wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
delivered_pack_id = new_picking
back_order_name = self.browse(cr, uid, delivered_pack_id, context=context).name
self.back_order_send_note(cr, uid, ids, back_order_name, context)
else:
- self.action_move(cr, uid, [pick.id])
+ self.action_move(cr, uid, [pick.id], context=context)
wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_done', cr)
delivered_pack_id = pick.id
self.ship_done_send_note(cr, uid, ids, context)
@@ -1553,7 +1553,7 @@ class stock_production_lot(osv.osv):
'product_id': lambda x, y, z, c: c.get('product_id', False),
}
_sql_constraints = [
- ('name_ref_uniq', 'unique (name, ref)', 'The combination of serial number and internal reference must be unique !'),
+ ('name_ref_uniq', 'unique (name, ref)', 'The combination of Serial Number and internal reference must be unique !'),
]
def action_traceability(self, cr, uid, ids, context=None):
""" It traces the information of a product
@@ -2123,7 +2123,7 @@ class stock_move(osv.osv):
old_ptype = location_obj.picking_type_get(cr, uid, picking.move_lines[0].location_id, picking.move_lines[0].location_dest_id)
if old_ptype != picking.type:
old_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + old_ptype)
- self.pool.get('stock.picking').write(cr, uid, [picking.id], {'name': old_pick_name}, context=context)
+ self.pool.get('stock.picking').write(cr, uid, [picking.id], {'name': old_pick_name, 'type': old_ptype}, context=context)
else:
pickid = False
for move, (loc, dummy, delay, dummy, company_id, ptype) in todo:
@@ -2433,14 +2433,17 @@ class stock_move(osv.osv):
if move.picking_id:
picking_ids.append(move.picking_id.id)
if move.move_dest_id.id and (move.state != 'done'):
- self.write(cr, uid, [move.id], {'move_history_ids': [(4, move.move_dest_id.id)]})
- #cr.execute('insert into stock_move_history_ids (parent_id,child_id) values (%s,%s)', (move.id, move.move_dest_id.id))
- if move.move_dest_id.state in ('waiting', 'confirmed'):
- self.force_assign(cr, uid, [move.move_dest_id.id], context=context)
- if move.move_dest_id.picking_id:
- wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
- if move.move_dest_id.auto_validate:
- self.action_done(cr, uid, [move.move_dest_id.id], context=context)
+ # Downstream move should only be triggered if this move is the last pending upstream move
+ other_upstream_move_ids = self.search(cr, uid, [('id','!=',move.id),('state','not in',['done','cancel']),
+ ('move_dest_id','=',move.move_dest_id.id)], context=context)
+ if not other_upstream_move_ids:
+ self.write(cr, uid, [move.id], {'move_history_ids': [(4, move.move_dest_id.id)]})
+ if move.move_dest_id.state in ('waiting', 'confirmed'):
+ self.force_assign(cr, uid, [move.move_dest_id.id], context=context)
+ if move.move_dest_id.picking_id:
+ wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
+ if move.move_dest_id.auto_validate:
+ self.action_done(cr, uid, [move.move_dest_id.id], context=context)
self._create_product_valuation_moves(cr, uid, move, context=context)
if move.state not in ('confirmed','done','assigned'):
diff --git a/addons/stock/stock_sequence.xml b/addons/stock/stock_sequence.xml
index 6c0b3e84cf2..c3569c48c3e 100644
--- a/addons/stock/stock_sequence.xml
+++ b/addons/stock/stock_sequence.xml
@@ -17,7 +17,7 @@
Picking INT
- stock.picking.internal
+ stock.picking
"
+ cr.execute('SELECT id FROM ir_module_module WHERE name = \'auth_openid\' and state = \'installed\'')
+ if cr.rowcount:
+ cr.execute( 'SELECT password, id FROM res_users WHERE login=%s AND openid_key = %s AND active',
+ (login.encode('utf-8'),password.encode('utf-8')))
+ if cr.rowcount:
+ # Check if the encrypted password matches against the one in the db.
+ cr.execute("""UPDATE res_users
+ SET login_date=now() AT TIME ZONE 'UTC'
+ WHERE id=%s AND openid_key=%s AND active
+ RETURNING id""",
+ (int(id), password.encode('utf-8')))
+ res = cr.fetchone()
+ cr.commit()
+
+ if res:
+ return res[0]
stored_pw = self.maybe_encrypt(cr, stored_pw, id)
@@ -235,7 +252,6 @@ class users(osv.osv):
if not passwd:
# empty passwords disallowed for obvious security reasons
raise security.ExceptionNoTb('AccessDenied')
-
# Get a chance to hash all passwords in db before using the uid_cache.
obj = pooler.get_pool(db).get('res.users')
if not hasattr(obj, "_salt_cache"):
From b7bb7e624639c7be098ecb19d09ab4d9a50f4af5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?=
Date: Thu, 15 Nov 2012 15:30:47 +0100
Subject: [PATCH 30/43] [IMP] added a .ie class on when using ie <= 9
bzr revid: fva@openerp.com-20121115143047-utonsnq0iadbvmyt
---
addons/web/controllers/main.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index 407b87b6ab1..717ede144eb 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -545,7 +545,8 @@ def content_disposition(filename, req):
#----------------------------------------------------------
html_template = """
-
+
+
From 3dae59cabdcb6e0bf37fad5b98fe091df925d0d3 Mon Sep 17 00:00:00 2001
From: "Denis Ledoux dle@openerp.com" <>
Date: Thu, 15 Nov 2012 15:44:55 +0100
Subject: [PATCH 31/43] [FIX]7.0 bookmark module - not working correctly
lp bug: https://launchpad.net/bugs/1077138 fixed
bzr revid: dle@openerp.com-20121115144455-gdpss6sp4ul5zut3
---
addons/web_shortcuts/static/src/js/web_shortcuts.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/web_shortcuts/static/src/js/web_shortcuts.js b/addons/web_shortcuts/static/src/js/web_shortcuts.js
index e7cbbaccef6..583069cc3d1 100644
--- a/addons/web_shortcuts/static/src/js/web_shortcuts.js
+++ b/addons/web_shortcuts/static/src/js/web_shortcuts.js
@@ -54,7 +54,7 @@ instance.web_shortcuts.Shortcuts = instance.web.Widget.extend({
},
add: function (sc) {
var self = this;
- this.dataset.create(sc, function (out) {
+ this.dataset.create(sc).then(function(out){
self.trigger('display', {
name : sc.name,
id : out.result,
From 27c672b1fc1556602764d066de4b63cdb1eeb158 Mon Sep 17 00:00:00 2001
From: Arnaud Pineux
Date: Thu, 15 Nov 2012 16:09:45 +0100
Subject: [PATCH 32/43] [FIX]
bzr revid: api@openerp.com-20121115150945-6zf30o1hk56bv5sf
---
.../account_accountant_data.xml | 4 ++--
addons/account_asset/account_asset.py | 18 +++++++-------
addons/base_crypt/crypt.py | 22 ++----------------
addons/crm/crm_data.xml | 2 +-
addons/fleet/static/src/img/icon.png | Bin 11334 -> 23083 bytes
addons/hr_evaluation/hr_evaluation_data.xml | 4 ++--
addons/hr_recruitment/hr_recruitment_data.xml | 2 +-
addons/mail/data/mail_group_data.xml | 4 ++--
addons/mail/mail_group.py | 2 +-
addons/mail/static/src/css/mail.css | 5 +++-
addons/mail/static/src/css/mail_group.css | 2 +-
addons/mrp/mrp_data.xml | 2 +-
addons/note/__openerp__.py | 1 +
addons/project/project_data.xml | 2 +-
addons/project_gtd/__openerp__.py | 2 +-
addons/project_gtd/project_gtd_data.xml | 2 +-
addons/project_issue/project_issue_data.xml | 4 ++--
addons/project_issue/project_issue_demo.xml | 10 ++++----
addons/sale/sale_data.xml | 2 +-
addons/web_linkedin/static/src/js/linkedin.js | 4 ++--
20 files changed, 41 insertions(+), 53 deletions(-)
diff --git a/addons/account_accountant/account_accountant_data.xml b/addons/account_accountant/account_accountant_data.xml
index 14d8f59a5fe..c7d6eb80bc6 100644
--- a/addons/account_accountant/account_accountant_data.xml
+++ b/addons/account_accountant/account_accountant_data.xml
@@ -28,8 +28,8 @@
notification
Accounting and Finance application installed!
- With OpenERP's accounting, you get instant access to your financial data, and can setup analytic accounting, forecast taxes, control budgets, easily create and send invoices, record bank statements, etc.
-The accounting features are fully integrated with other OpenERP applications to automate all your processes: creation of customer invoices, control of supplier invoices, point-of-sale integration, automated follow-ups, etc.
]]>
+ With OpenERP's accounting, you get instant access to your financial data, and can setup analytic accounting, forecast taxes, control budgets, easily create and send invoices, record bank statements, etc.
+The accounting features are fully integrated with other OpenERP applications to automate all your processes: creation of customer invoices, control of supplier invoices, point-of-sale integration, automated follow-ups, etc.
]]>
diff --git a/addons/account_asset/account_asset.py b/addons/account_asset/account_asset.py
index 0dddf441c59..a44226e76c8 100644
--- a/addons/account_asset/account_asset.py
+++ b/addons/account_asset/account_asset.py
@@ -377,9 +377,8 @@ class account_asset_depreciation_line(osv.osv):
move_line_obj = self.pool.get('account.move.line')
currency_obj = self.pool.get('res.currency')
created_move_ids = []
+ asset_ids = []
for line in self.browse(cr, uid, ids, context=context):
- if currency_obj.is_zero(cr, uid, line.asset_id.currency_id, line.remaining_value):
- can_close = True
depreciation_date = time.strftime('%Y-%m-%d')
period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
company_currency = line.asset_id.company_id.currency_id.id
@@ -409,8 +408,8 @@ class account_asset_depreciation_line(osv.osv):
'period_id': period_ids and period_ids[0] or False,
'journal_id': journal_id,
'partner_id': partner_id,
- 'currency_id': company_currency <> current_currency and current_currency or False,
- 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
+ 'currency_id': company_currency != current_currency and current_currency or False,
+ 'amount_currency': company_currency != current_currency and - sign * line.amount or 0.0,
'date': depreciation_date,
})
move_line_obj.create(cr, uid, {
@@ -423,16 +422,19 @@ class account_asset_depreciation_line(osv.osv):
'period_id': period_ids and period_ids[0] or False,
'journal_id': journal_id,
'partner_id': partner_id,
- 'currency_id': company_currency <> current_currency and current_currency or False,
- 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
+ 'currency_id': company_currency != current_currency and current_currency or False,
+ 'amount_currency': company_currency != current_currency and sign * line.amount or 0.0,
'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
'date': depreciation_date,
'asset_id': line.asset_id.id
})
self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
created_move_ids.append(move_id)
- if can_close:
- asset_obj.write(cr, uid, [line.asset_id.id], {'state': 'close'}, context=context)
+ asset_ids.append(line.asset_id.id)
+ # we re-evaluate the assets to determine whether we can close them
+ for asset in asset_obj.browse(cr, uid, list(set(asset_ids)), context=context):
+ if currency_obj.is_zero(cr, uid, asset.currency_id, asset.value_residual):
+ asset.write({'state': 'close'})
return created_move_ids
account_asset_depreciation_line()
diff --git a/addons/base_crypt/crypt.py b/addons/base_crypt/crypt.py
index bdb3521ec63..14e029eb988 100644
--- a/addons/base_crypt/crypt.py
+++ b/addons/base_crypt/crypt.py
@@ -43,7 +43,6 @@ import pooler
from tools.translate import _
from service import security
import logging
-import pdb
magic_md5 = '$1$'
_logger = logging.getLogger(__name__)
@@ -184,8 +183,7 @@ class users(osv.osv):
cr = None
try:
cr = pooler.get_db(db).cursor()
- test = self._login(cr, db, login, password)
- return test
+ return self._login(cr, db, login, password)
except Exception:
_logger.exception('Cannot authenticate.')
return Exception('Access denied.')
@@ -203,23 +201,6 @@ class users(osv.osv):
# Return early if no one has a login name like that.
return False
- cr.execute('SELECT id FROM ir_module_module WHERE name = \'auth_openid\' and state = \'installed\'')
- if cr.rowcount:
- cr.execute( 'SELECT password, id FROM res_users WHERE login=%s AND openid_key = %s AND active',
- (login.encode('utf-8'),password.encode('utf-8')))
- if cr.rowcount:
- # Check if the encrypted password matches against the one in the db.
- cr.execute("""UPDATE res_users
- SET login_date=now() AT TIME ZONE 'UTC'
- WHERE id=%s AND openid_key=%s AND active
- RETURNING id""",
- (int(id), password.encode('utf-8')))
- res = cr.fetchone()
- cr.commit()
-
- if res:
- return res[0]
-
stored_pw = self.maybe_encrypt(cr, stored_pw, id)
if not stored_pw:
@@ -252,6 +233,7 @@ class users(osv.osv):
if not passwd:
# empty passwords disallowed for obvious security reasons
raise security.ExceptionNoTb('AccessDenied')
+
# Get a chance to hash all passwords in db before using the uid_cache.
obj = pooler.get_pool(db).get('res.users')
if not hasattr(obj, "_salt_cache"):
diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml
index 91117d2d83e..f2bf56e34ac 100644
--- a/addons/crm/crm_data.xml
+++ b/addons/crm/crm_data.xml
@@ -61,7 +61,7 @@
CRM application installed!
From the top Sales menu you can track leads and opportunities, get accurate forecast on your sales pipeline, plan meetings and phonecalls, get realtime statistics and efficiently organize the communication with your prospects.
- To manage quotations and sale orders, install the "Sales Management" application.]]>
+To manage quotations and sale orders, install the "Sales Management" application.
]]>
diff --git a/addons/fleet/static/src/img/icon.png b/addons/fleet/static/src/img/icon.png
index 3db7ae9fa4962307aae3d3e446abfa47329ecc39..5b41e43c5bddb80c5b13b6a9e04ae6f0725163f5 100644
GIT binary patch
literal 23083
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
MK?6}EXBqZ?0O=Vi(*OVf
diff --git a/addons/hr_evaluation/hr_evaluation_data.xml b/addons/hr_evaluation/hr_evaluation_data.xml
index 45100b14e2a..2afb5200091 100644
--- a/addons/hr_evaluation/hr_evaluation_data.xml
+++ b/addons/hr_evaluation/hr_evaluation_data.xml
@@ -811,8 +811,8 @@ Once the form had been filled, the employee send it to his supervisor.
The comment you entered is in an invalid format.
* His direct reports will be invited through OpenERP to express a feedback on their supervisor's leadership and to give their opinion about their own engagement and effectiveness, the continuous improvement and openness in action in the company, ...
-* The employees will send back their anonymous answers to OpenERP. The data will be handled by the HR manager and a brief summary of the data will be sent to the concerned supervisor, to his team and to the supervisor's supervisor.
-* The appraiser should rate the employee’s major work accomplishments and performance according to the metric provided below :
+* The employees will send back their anonymous answers to OpenERP. The data will be handled by the HR manager and a brief summary of the data will be sent to the concerned supervisor, to his team and to the supervisor's supervisor.
+* The appraiser should rate the employee’s major work accomplishments and performance according to the metric provided below :
1 - Significantly exceeds standards and expectations required of the position
2 - Exceeds standards and expectations
diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml
index f1c8ed46aca..adaeed81205 100644
--- a/addons/hr_recruitment/hr_recruitment_data.xml
+++ b/addons/hr_recruitment/hr_recruitment_data.xml
@@ -8,7 +8,7 @@
notification
Recruitment Process application installed!
- Manage job positions and your company's recruitment process. This application is integrated with the Survey application to help you define interviews for different jobs.
+ Manage job positions and your company's recruitment process. This application is integrated with the Survey application to help you define interviews for different jobs.
You can automatically receive job application though an email gateway, see the Human Resources settings.
]]>
diff --git a/addons/mail/data/mail_group_data.xml b/addons/mail/data/mail_group_data.xml
index aae30226bf2..64d989f9e9d 100644
--- a/addons/mail/data/mail_group_data.xml
+++ b/addons/mail/data/mail_group_data.xml
@@ -19,8 +19,8 @@
notification
Welcome to OpenERP!
- Your homepage is a summary of messages you received and key information about documents you follow.
-The top menu bar contains all applications you installed. You can use this <i>Settings</i> menu to install more applications, activate others features or give access to new users.
+ Your homepage is a summary of messages you received and key information about documents you follow.
+The top menu bar contains all applications you installed. You can use the Settings menu to install more applications, activate others features or give access to new users.
To setup your preferences (name, email signature, avatar), click on the top right corner.
]]>
diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py
index d4c9c565bf3..b163882577b 100644
--- a/addons/mail/mail_group.py
+++ b/addons/mail/mail_group.py
@@ -130,7 +130,7 @@ class mail_group(osv.Model):
params = {
'search_view_id': search_ref and search_ref[1] or False,
'domain': [('model', '=', 'mail.group'), ('res_id', '=', mail_group_id)],
- 'context': {'default_model': 'mail.group', 'default_res_id': mail_group_id},
+ 'context': {'default_model': 'mail.group', 'default_res_id': mail_group_id, 'search_default_message_unread': True},
'res_model': 'mail.message',
'thread_level': 1,
}
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index 0bcfd0d3733..b0ce6d499b5 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -87,8 +87,11 @@
margin-bottom: 0px;
margin-top: 2px;
}
-.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body p{
+.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body p:first-of-type {
margin-top: 0px;
+}
+
+.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body p {
margin-bottom: 0px;
}
diff --git a/addons/mail/static/src/css/mail_group.css b/addons/mail/static/src/css/mail_group.css
index 467bc53d80f..c1cefee9070 100644
--- a/addons/mail/static/src/css/mail_group.css
+++ b/addons/mail/static/src/css/mail_group.css
@@ -52,7 +52,7 @@
border-collapse: separate;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- box-shadow: 0 1px 4px 3px rgba(0, 0, 0, 0.4);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
diff --git a/addons/mrp/mrp_data.xml b/addons/mrp/mrp_data.xml
index 1d92609eb7e..a6b65b7c475 100644
--- a/addons/mrp/mrp_data.xml
+++ b/addons/mrp/mrp_data.xml
@@ -8,7 +8,7 @@
notification
MRP application installed!
- Manage your manufacturing process with OpenERP by defining your bills of materials (BoM), routings and work centers.
+ Manage your manufacturing process with OpenERP by defining your bills of materials (BoM), routings and work centers.
This application supports complete integration and production scheduling for stockable goods, consumables, and services.
From the Manufacturing Settings, you can choose to compute production schedules periodically or just-in-time.
]]>
diff --git a/addons/note/__openerp__.py b/addons/note/__openerp__.py
index d87ee70086d..797270637e2 100644
--- a/addons/note/__openerp__.py
+++ b/addons/note/__openerp__.py
@@ -38,6 +38,7 @@ Notes can be found in the 'Home' menu.
'author': 'OpenERP SA',
'website': 'http://openerp.com',
'summary': 'Sticky notes, Collaborative, Memos',
+ 'sequence': 9,
'depends': [
'mail',
],
diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml
index c5980ecddd2..08981bd126a 100644
--- a/addons/project/project_data.xml
+++ b/addons/project/project_data.xml
@@ -128,7 +128,7 @@
Project Management application installed!
Manage multi-level projects and tasks. You can delegate tasks, track task work, and review your planning.
-You can manage todo lists on tasks by installing the "Todo Lists" application, supporting the Getting Things Done (GTD) methodology.
+You can manage todo lists on tasks by installing the Todo Lists application, supporting the Getting Things Done (GTD) methodology.
You can also manage issues/bugs in projects by installing the "Issue Tracker" application.
]]>
diff --git a/addons/project_gtd/__openerp__.py b/addons/project_gtd/__openerp__.py
index 441f8859dc6..c99535f63fd 100644
--- a/addons/project_gtd/__openerp__.py
+++ b/addons/project_gtd/__openerp__.py
@@ -24,7 +24,7 @@
'name': 'Todo Lists',
'version': '1.0',
'category': 'Project Management',
- 'sequence': 9,
+ 'sequence': 100,
'summary': 'Personal Tasks, Contexts, Timeboxes',
'description': """
Implement concepts of the "Getting Things Done" methodology
diff --git a/addons/project_gtd/project_gtd_data.xml b/addons/project_gtd/project_gtd_data.xml
index 1536ac4be3f..9d8c99f23ff 100644
--- a/addons/project_gtd/project_gtd_data.xml
+++ b/addons/project_gtd/project_gtd_data.xml
@@ -32,7 +32,7 @@
notification
Todo Lists application installed!
- Add todo items on project tasks, to help you organize your work.
+ Add todo items on project tasks, to help you organize your work.
This application supports the Getting Things Done (GTD) methodology, based on David Allen's book.
]]>
diff --git a/addons/project_issue/project_issue_data.xml b/addons/project_issue/project_issue_data.xml
index d70cd71e240..c1460167097 100644
--- a/addons/project_issue/project_issue_data.xml
+++ b/addons/project_issue/project_issue_data.xml
@@ -38,8 +38,8 @@
notification
Issue Tracker application installed!
- Manage the issues you might face in a project, such as bugs in a system, client complaints or material breakdowns.
-You can record issues, assign them to a responsible person, and keep track of their status as they evolve over time.
+ Manage the issues you might face in a project, such as bugs in a system, client complaints or material breakdowns.
+You can record issues, assign them to a responsible person, and keep track of their status as they evolve over time.
Access all issues from the top Project menu, and access the issues of a specific project via the projects gallery view.
]]>
diff --git a/addons/project_issue/project_issue_demo.xml b/addons/project_issue/project_issue_demo.xml
index 86e4c59b571..63fa37c84c3 100644
--- a/addons/project_issue/project_issue_demo.xml
+++ b/addons/project_issue/project_issue_demo.xml
@@ -2,19 +2,19 @@
-
+
-
+
-
+
-
+
-
+
diff --git a/addons/sale/sale_data.xml b/addons/sale/sale_data.xml
index d5ed8e810d6..3fe03966da2 100644
--- a/addons/sale/sale_data.xml
+++ b/addons/sale/sale_data.xml
@@ -41,7 +41,7 @@
Sales Management application installed!
This application lets you create and send quotations and process your sales orders; from delivery to invoicing.
-If you need to manage your sales pipeline (leads, opportunities, phonecalls), the <i>CRM</i> application may be useful. Use the Settings menu to install it.
]]>
+If you need to manage your sales pipeline (leads, opportunities, phonecalls), the CRM application may be useful. Use the Settings menu to install it.
]]>
diff --git a/addons/web_linkedin/static/src/js/linkedin.js b/addons/web_linkedin/static/src/js/linkedin.js
index ec58c3736f4..d6237e48d51 100644
--- a/addons/web_linkedin/static/src/js/linkedin.js
+++ b/addons/web_linkedin/static/src/js/linkedin.js
@@ -20,7 +20,7 @@ openerp.web_linkedin = function(instance) {
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = "http://platform.linkedin.com/in.js";
- tag.innerHTML = 'api_key : ' + self.api_key + '\nauthorize : true';
+ tag.innerHTML = 'api_key : ' + self.api_key + '\nauthorize : true\nscope: r_network r_contactinfo';
document.getElementsByTagName('head')[0].appendChild(tag);
self.linkedin_added = true;
$(tag).load(function() {
@@ -107,7 +107,7 @@ openerp.web_linkedin = function(instance) {
}
to_change.website = entity.websiteUrl;
to_change.phone = false;
- _.each(entity.locations.values || [], function(el) {
+ _.each((entity.locations || {}).values || [], function(el) {
to_change.phone = el.contactInfo.phone1;
});
var children_def = $.Deferred();
From 8a3d2c474483a79e1e4efbc067881a91d2f7a2bd Mon Sep 17 00:00:00 2001
From: Fabien Meghazi
Date: Thu, 15 Nov 2012 16:10:46 +0100
Subject: [PATCH 33/43] [FIX] For act window target new, always use a new
dialog object
Buttons footer hack in dialogs makes reusability of dialog impossible
bzr revid: fme@openerp.com-20121115151046-0r8xz85racjta7p3
---
addons/web/static/src/js/views.js | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 6a0294c3d88..d804bd57cf8 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -25,6 +25,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
dialog_stop: function () {
if (this.dialog) {
this.dialog.destroy();
+ this.dialog = null;
}
},
/**
@@ -310,15 +311,15 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
var widget = executor.widget();
if (executor.action.target === 'new') {
- if (this.dialog_widget && ! this.dialog_widget.isDestroyed())
+ if (this.dialog_widget && !this.dialog_widget.isDestroyed()) {
this.dialog_widget.destroy();
- if (this.dialog === null || this.dialog.isDestroyed()) {
- this.dialog = new instance.web.Dialog(this, {
- dialogClass: executor.klass,
- });
- this.dialog.on("closing", null, options.on_close);
- this.dialog.init_dialog();
}
+ this.dialog_stop();
+ this.dialog = new instance.web.Dialog(this, {
+ dialogClass: executor.klass,
+ });
+ this.dialog.on("closing", null, options.on_close);
+ this.dialog.init_dialog();
this.dialog.dialog_title = executor.action.name;
if (widget instanceof instance.web.ViewManager) {
_.extend(widget.flags, {
From 69f385414a86154a2486956e58fd174e7ac349bd Mon Sep 17 00:00:00 2001
From: Christophe Simonis
Date: Thu, 15 Nov 2012 16:12:28 +0100
Subject: [PATCH 34/43] [FIX] manifest_glob return path with / for web part
bzr revid: chs@openerp.com-20121115151228-xzrlpjxwkspf5fgz
---
addons/web/controllers/main.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index 717ede144eb..0b3d5341b11 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -263,6 +263,10 @@ def concat_js(file_list):
content = rjsmin(content)
return content, checksum
+def fs2web(path):
+ """convert FS path into web path"""
+ return '/'.join(path.split(os.path.sep))
+
def manifest_glob(req, addons, key):
if addons is None:
addons = module_boot(req)
@@ -278,7 +282,7 @@ def manifest_glob(req, addons, key):
globlist = manifest.get(key, [])
for pattern in globlist:
for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))):
- r.append((path, path[len(addons_path):]))
+ r.append((path, fs2web(path[len(addons_path):])))
return r
def manifest_list(req, mods, extension):
@@ -638,8 +642,7 @@ class WebClient(openerpweb.Controller):
data = fp.read().decode('utf-8')
path = file_map[f]
- # convert FS path into web path
- web_dir = '/'.join(os.path.dirname(path).split(os.path.sep))
+ web_dir = os.path.dirname(path)
data = re.sub(
rx_import,
From 87899acfa8128b636fd3c1932e63fe3eccaa874c Mon Sep 17 00:00:00 2001
From: Arnaud Pineux
Date: Thu, 15 Nov 2012 16:14:10 +0100
Subject: [PATCH 35/43] [FIX] openID and Crypt
lp bug: https://launchpad.net/bugs/1026784 fixed
bzr revid: api@openerp.com-20121115151410-b2mey3wystp5zha0
---
addons/base_crypt/crypt.py | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/addons/base_crypt/crypt.py b/addons/base_crypt/crypt.py
index 14e029eb988..a1c7519958c 100644
--- a/addons/base_crypt/crypt.py
+++ b/addons/base_crypt/crypt.py
@@ -201,6 +201,20 @@ class users(osv.osv):
# Return early if no one has a login name like that.
return False
+ # Check if the authentification is made with OpenID. In this case, don't encrypt the password
+ cr.execute('SELECT id FROM ir_module_module WHERE name = \'auth_openid\' and state = \'installed\'')
+ if cr.rowcount:
+ cr.execute( 'SELECT password, id FROM res_users WHERE login=%s AND openid_key = %s AND active',
+ (login.encode('utf-8'),password.encode('utf-8')))
+ if cr.rowcount:
+ # Check if the encrypted password matches against the one in the db.
+ cr.execute("""UPDATE res_users SET login_date=now() AT TIME ZONE 'UTC' WHERE id=%s AND openid_key=%s AND active RETURNING id""",
+ (int(id), password.encode('utf-8')))
+ res = cr.fetchone()
+ cr.commit()
+ if res:
+ return res[0]
+
stored_pw = self.maybe_encrypt(cr, stored_pw, id)
if not stored_pw:
From bba12d9e95d681e17d2898ea598ac0f1f4e90207 Mon Sep 17 00:00:00 2001
From: "Denis Ledoux dle@openerp.com" <>
Date: Thu, 15 Nov 2012 16:57:08 +0100
Subject: [PATCH 36/43] [FIX]wrong out id when adding shortcut to system tray
list
lp bug: https://launchpad.net/bugs/1077138 fixed
bzr revid: dle@openerp.com-20121115155708-vbz1djvtp15dit78
---
addons/web_shortcuts/static/src/js/web_shortcuts.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/addons/web_shortcuts/static/src/js/web_shortcuts.js b/addons/web_shortcuts/static/src/js/web_shortcuts.js
index 583069cc3d1..04fa09e300c 100644
--- a/addons/web_shortcuts/static/src/js/web_shortcuts.js
+++ b/addons/web_shortcuts/static/src/js/web_shortcuts.js
@@ -57,7 +57,7 @@ instance.web_shortcuts.Shortcuts = instance.web.Widget.extend({
this.dataset.create(sc).then(function(out){
self.trigger('display', {
name : sc.name,
- id : out.result,
+ id : out,
res_id : sc.res_id
});
});
@@ -108,24 +108,24 @@ instance.web.UserMenu.include({
});
instance.web.ViewManagerAction.include({
- switch_mode: function (view_type, no_store) {
+ switch_mode: function () {
var self = this;
this._super.apply(this, arguments).done(function() {
- self.shortcut_check(self.views[view_type]);
+ self.shortcut_check();
});
},
- shortcut_check : function(view) {
+ shortcut_check : function() {
var self = this;
var shortcuts_menu = instance.webclient.user_menu.shortcuts;
var grandparent = this.getParent() && this.getParent().getParent();
// display shortcuts if on the first view for the action
var $shortcut_toggle = this.$el.find('.oe_shortcuts_toggle');
- if (!this.action.name ||
+ /*if (!this.action.name ||
!(view.view_type === this.views_src[0].view_type
&& view.view_id === this.views_src[0].view_id)) {
$shortcut_toggle.hide();
return;
- }
+ }*/
$shortcut_toggle.toggleClass('oe_shortcuts_remove', shortcuts_menu.has(self.session.active_id));
$shortcut_toggle.unbind("click").click(function() {
if ($shortcut_toggle.hasClass("oe_shortcuts_remove")) {
From 0cde689fa9ea3da43c8e7d569b7b9bac0362225b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Date: Thu, 15 Nov 2012 17:00:08 +0100
Subject: [PATCH 37/43] [FIX] fetchmail: fixed sending email on res.users
aliases. This is done by using the message_update method of the related
partner.
bzr revid: tde@openerp.com-20121115160008-gpz50ryhdw7h3f8b
---
addons/mail/res_users.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py
index fcca12024b7..20c0715c148 100644
--- a/addons/mail/res_users.py
+++ b/addons/mail/res_users.py
@@ -141,6 +141,11 @@ class res_users(osv.Model):
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(cr, uid, partner_id, context=context, **kwargs)
+ def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
+ partner_id = self.pool.get('res.users').browse(cr, uid, ids)[0].partner_id.id
+ return self.pool.get('res.partner').message_update(cr, uid, [partner_id], msg_dict,
+ update_vals=update_vals, context=context)
+
class res_users_mail_group(osv.Model):
""" Update of res.users class
From e72542b6ecf4efdcb1539abc29e065bc8a1ec5a8 Mon Sep 17 00:00:00 2001
From: "Denis Ledoux dle@openerp.com" <>
Date: Thu, 15 Nov 2012 17:05:50 +0100
Subject: [PATCH 38/43] [FIX]Reload whole favorites (by rpc call) instead of
adding the new one, otherwise the name of the new starred item is not
structured like the other ones
lp bug: https://launchpad.net/bugs/1077138 fixed
bzr revid: dle@openerp.com-20121115160550-p5kc3j8swxu3lfv0
---
.../web_shortcuts/static/src/js/web_shortcuts.js | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/addons/web_shortcuts/static/src/js/web_shortcuts.js b/addons/web_shortcuts/static/src/js/web_shortcuts.js
index 04fa09e300c..918feb4634d 100644
--- a/addons/web_shortcuts/static/src/js/web_shortcuts.js
+++ b/addons/web_shortcuts/static/src/js/web_shortcuts.js
@@ -55,11 +55,7 @@ instance.web_shortcuts.Shortcuts = instance.web.Widget.extend({
add: function (sc) {
var self = this;
this.dataset.create(sc).then(function(out){
- self.trigger('display', {
- name : sc.name,
- id : out,
- res_id : sc.res_id
- });
+ self.trigger('load');
});
},
display: function(sc) {
@@ -108,24 +104,24 @@ instance.web.UserMenu.include({
});
instance.web.ViewManagerAction.include({
- switch_mode: function () {
+ switch_mode: function (view_type, no_store) {
var self = this;
this._super.apply(this, arguments).done(function() {
- self.shortcut_check();
+ self.shortcut_check(self.views[view_type]);
});
},
- shortcut_check : function() {
+ shortcut_check : function(view) {
var self = this;
var shortcuts_menu = instance.webclient.user_menu.shortcuts;
var grandparent = this.getParent() && this.getParent().getParent();
// display shortcuts if on the first view for the action
var $shortcut_toggle = this.$el.find('.oe_shortcuts_toggle');
- /*if (!this.action.name ||
+ if (!this.action.name ||
!(view.view_type === this.views_src[0].view_type
&& view.view_id === this.views_src[0].view_id)) {
$shortcut_toggle.hide();
return;
- }*/
+ }
$shortcut_toggle.toggleClass('oe_shortcuts_remove', shortcuts_menu.has(self.session.active_id));
$shortcut_toggle.unbind("click").click(function() {
if ($shortcut_toggle.hasClass("oe_shortcuts_remove")) {
From cace9f78878ca3e3e143174220dfdfd306d13c86 Mon Sep 17 00:00:00 2001
From: Fabien Meghazi
Date: Thu, 15 Nov 2012 17:41:32 +0100
Subject: [PATCH 39/43] [IMP] Centralized 'openerp' class on top level
elements. Also add openerp_ie class for MSIE
bzr revid: fme@openerp.com-20121115164132-i6a0u8zk3knm1zkv
---
addons/web/controllers/main.py | 3 +--
addons/web/static/lib/jquery.tipsy/jquery.tipsy.js | 3 ++-
addons/web/static/src/js/chrome.js | 3 ++-
addons/web/static/src/js/coresetup.js | 10 ++++++++++
addons/web/static/src/js/view_form.js | 2 +-
5 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index 0b3d5341b11..cfdad2e3730 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -549,8 +549,7 @@ def content_disposition(filename, req):
#----------------------------------------------------------
html_template = """
-
-
+
diff --git a/addons/web/static/lib/jquery.tipsy/jquery.tipsy.js b/addons/web/static/lib/jquery.tipsy/jquery.tipsy.js
index 5929d591261..e978042f21b 100644
--- a/addons/web/static/lib/jquery.tipsy/jquery.tipsy.js
+++ b/addons/web/static/lib/jquery.tipsy/jquery.tipsy.js
@@ -27,7 +27,8 @@
var $tip = this.tip();
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
- $tip[0].className = 'tipsy openerp oe_tooltip '; // reset classname in case of dynamic gravity
+ $tip[0].className = 'tipsy '; // reset classname in case of dynamic gravity
+ $tip.openerpClass('oe_tooltip');
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
var pos = $.extend({}, this.$element.offset(), {
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index f5fc7b8f632..941d26db6c6 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -48,7 +48,7 @@ instance.web.Notification = instance.web.Widget.extend({
*/
instance.web.dialog = function(element) {
var result = element.dialog.apply(element, _.rest(_.toArray(arguments)));
- result.dialog("widget").addClass("openerp");
+ result.dialog("widget").openerpClass();
return result;
};
@@ -983,6 +983,7 @@ instance.web.Client = instance.web.Widget.extend({
return instance.session.session_bind(this.origin).then(function() {
var $e = $(QWeb.render(self._template, {}));
self.replaceElement($e);
+ $e.openerpClass();
self.bind_events();
return self.show_common();
});
diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js
index f3166285fe7..7789bb48785 100644
--- a/addons/web/static/src/js/coresetup.js
+++ b/addons/web/static/src/js/coresetup.js
@@ -460,6 +460,16 @@ $.fn.getAttributes = function() {
}
return o;
}
+$.fn.openerpClass = function(additionalClass) {
+ // This plugin should be applied on top level elements
+ additionalClass = additionalClass || '';
+ if (!!$.browser.msie) {
+ additionalClass += ' openerp_ie';
+ }
+ return this.each(function() {
+ $(this).addClass('openerp ' + additionalClass);
+ });
+};
/** Jquery extentions */
$.Mutex = (function() {
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index a955da06eea..ffdc49513b8 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -3122,7 +3122,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
minLength: 0,
delay: 0
});
- this.$input.autocomplete("widget").addClass("openerp");
+ this.$input.autocomplete("widget").openerpClass();
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
this.$input.keyup(function(e) {
if (e.which === 13) { // ENTER
From 801927cb14b267d4cfadfb9f316572218d41296d Mon Sep 17 00:00:00 2001
From: Christophe Simonis
Date: Thu, 15 Nov 2012 18:12:01 +0100
Subject: [PATCH 40/43] [FIX] make root menu links works
bzr revid: chs@openerp.com-20121115171201-76y7ul1jhjyi2e78
---
addons/web/static/src/js/chrome.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 941d26db6c6..ea087f43679 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -1160,10 +1160,10 @@ instance.web.WebClient = instance.web.Client.extend({
var self = this;
var state = event.getState(true);
if (!_.isEqual(this._current_state, state)) {
- if(state.action === undefined && state.menu_id) {
+ if(!state.action && state.menu_id) {
self.menu.has_been_loaded.done(function() {
self.menu.do_reload().done(function() {
- self.menu.menu_click(state.menu_id)
+ self.menu.menu_click(state.menu_id);
});
});
} else {
From 54090e8d929f27738bfc4cc6d4412eff06bfbeef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Date: Thu, 15 Nov 2012 18:18:30 +0100
Subject: [PATCH 41/43] [FIX] incomign email on a partner: fixed auto-inclusion
of destination partner in recipients.
bzr revid: tde@openerp.com-20121115171830-0el4jrt1gjmtrts1
---
addons/mail/res_partner.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py
index 157699bd330..060adc291de 100644
--- a/addons/mail/res_partner.py
+++ b/addons/mail/res_partner.py
@@ -55,7 +55,7 @@ class res_partner_mail(osv.Model):
if type == 'email':
partner_ids = kwargs.get('partner_ids', [])
if thread_id not in partner_ids:
- partner_ids.append(thread_id)
+ partner_ids.append((4, thread_id))
kwargs['partner_ids'] = partner_ids
thread_id = False
return super(res_partner_mail, self).message_post(cr, uid, thread_id, body=body, subject=subject,
From 79e1118c165a197eb7056a7bbe3751537068a3fe Mon Sep 17 00:00:00 2001
From: Fabien Meghazi
Date: Thu, 15 Nov 2012 18:36:14 +0100
Subject: [PATCH 42/43] [IMP] Clean base.sass folding sections
bzr revid: fme@openerp.com-20121115173614-o5pchmm889yxz5cy
---
addons/web/static/src/css/base.css | 168 +++++++++++++---------------
addons/web/static/src/css/base.sass | 151 +++++++++++++------------
2 files changed, 155 insertions(+), 164 deletions(-)
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index 85e0a7db719..f530415740b 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -20,20 +20,6 @@
font-style: normal;
}
-@media print {
- .oe_topbar, .oe_leftbar, .oe_loading {
- display: none !important;
- }
-}
-.openerp.openerp_webclient_container {
- height: 100%;
-}
-
-.text-tag .text-button {
- height: auto !important;
- min-height: 16px;
-}
-
.openerp {
padding: 0;
margin: 0;
@@ -46,6 +32,9 @@
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
*/
}
+.openerp.openerp_webclient_container {
+ height: 100%;
+}
.openerp :-moz-placeholder {
color: #afafb6 !important;
font-style: italic !important;
@@ -197,6 +186,10 @@
.openerp .oe_bounce_container {
display: inline-block;
}
+.openerp .text-tag .text-button {
+ height: auto !important;
+ min-height: 16px;
+}
.openerp .ui-tabs {
position: static;
}
@@ -2361,6 +2354,77 @@
.openerp .oe_form .oe_form_field_image:hover .oe_form_field_image_controls {
display: block;
}
+.openerp .oe_fileupload {
+ display: inline-block;
+ clear: both;
+ width: 100%;
+}
+.openerp .oe_fileupload .oe_add {
+ float: left;
+ position: relative;
+ width: 100%;
+ left: 2px;
+ top: 7px;
+}
+.openerp .oe_fileupload .oe_add button {
+ display: inline;
+ height: 24px;
+ font-size: 12px;
+ line-height: 12px;
+ vertical-align: middle;
+}
+.openerp .oe_fileupload .oe_add button.oe_attach {
+ width: 24px;
+ overflow: hidden;
+ width: 24px;
+ overflow: hidden;
+ background: transparent;
+ color: #7c7bad;
+ box-shadow: none;
+ border: none;
+ text-shadow: none;
+}
+.openerp .oe_fileupload .oe_add button.oe_attach .oe_e {
+ position: relative;
+ top: -1px;
+ left: -9px;
+}
+.openerp .oe_fileupload .oe_add input.oe_form_binary_file {
+ display: inline-block;
+ margin-left: -5px;
+ height: 28px;
+ width: 52px;
+ margin-top: -26px;
+}
+.openerp .oe_fileupload .oe_add .oe_attach_label {
+ color: #7c7bad;
+ margin-left: -3px;
+}
+.openerp .oe_fileupload .oe_attachments {
+ margin-bottom: 4px;
+ margin-right: 0px;
+ font-size: 12px;
+ border-radius: 2px;
+ border: solid 1px rgba(124, 123, 173, 0.14);
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment {
+ padding: 2px;
+ padding-left: 4px;
+ padding-right: 4px;
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e {
+ font-size: 23px;
+ margin-top: -5px;
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e:hover {
+ text-decoration: none;
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(odd) {
+ background: white;
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(even) {
+ background: #f4f5fa;
+}
.openerp .oe_form_field_many2one td:first-child {
position: relative;
}
@@ -2893,78 +2957,6 @@
color: #333333;
}
-.openerp .oe_fileupload {
- display: inline-block;
- clear: both;
- width: 100%;
-}
-.openerp .oe_fileupload .oe_add {
- float: left;
- position: relative;
- width: 100%;
- left: 2px;
- top: 7px;
-}
-.openerp .oe_fileupload .oe_add button {
- display: inline;
- height: 24px;
- font-size: 12px;
- line-height: 12px;
- vertical-align: middle;
-}
-.openerp .oe_fileupload .oe_add button.oe_attach {
- width: 24px;
- overflow: hidden;
- width: 24px;
- overflow: hidden;
- background: transparent;
- color: #7c7bad;
- box-shadow: none;
- border: none;
- text-shadow: none;
-}
-.openerp .oe_fileupload .oe_add button.oe_attach .oe_e {
- position: relative;
- top: -1px;
- left: -9px;
-}
-.openerp .oe_fileupload .oe_add input.oe_form_binary_file {
- display: inline-block;
- margin-left: -5px;
- height: 28px;
- width: 52px;
- margin-top: -26px;
-}
-.openerp .oe_fileupload .oe_add .oe_attach_label {
- color: #7c7bad;
- margin-left: -3px;
-}
-.openerp .oe_fileupload .oe_attachments {
- margin-bottom: 4px;
- margin-right: 0px;
- font-size: 12px;
- border-radius: 2px;
- border: solid 1px rgba(124, 123, 173, 0.14);
-}
-.openerp .oe_fileupload .oe_attachments .oe_attachment {
- padding: 2px;
- padding-left: 4px;
- padding-right: 4px;
-}
-.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e {
- font-size: 23px;
- margin-top: -5px;
-}
-.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e:hover {
- text-decoration: none;
-}
-.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(odd) {
- background: white;
-}
-.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(even) {
- background: #f4f5fa;
-}
-
.kitten-mode-activated {
background-image: url(http://placekitten.com/g/1365/769);
background-size: cover;
@@ -3023,8 +3015,8 @@ div.ui-widget-overlay {
.openerp {
text-shadow: none;
}
- .openerp .oe_header_row, .openerp ul.oe_header, .openerp div.oe_mail_thread_action, .openerp .oe_mail_recthread_actions, .openerp .oe_button_box, .openerp .oe_form button, .openerp button.oe_invite, .openerp .oe_form header, .openerp .openerp .oe_notebook > li.ui-state-default {
- display: none;
+ .openerp .oe_header_row, .openerp ul.oe_header, .openerp div.oe_mail_thread_action, .openerp .oe_mail_recthread_actions, .openerp .oe_button_box, .openerp .oe_form button, .openerp button.oe_invite, .openerp .oe_form header, .openerp .openerp .oe_notebook > li.ui-state-default, .openerp .oe_topbar, .openerp .oe_leftbar, .openerp .oe_loading {
+ display: none !important;
}
.openerp .oe_list_content button, .openerp .oe_list_content input[type=checkbox] {
visibility: hidden;
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index 586bca407d0..7669af0ee2c 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -140,18 +140,6 @@ $sheet-padding: 16px
// }}}
-@media print
- .oe_topbar, .oe_leftbar, .oe_loading
- display: none !important
-
-.openerp.openerp_webclient_container
- height: 100%
-
-// jQueryUI css bug fixing
-.text-tag .text-button
- height: auto !important
- min-height: 16px
-
.openerp
// Global style {{{
padding: 0
@@ -161,6 +149,8 @@ $sheet-padding: 16px
font-size: 13px
background: white
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5)
+ &.openerp_webclient_container
+ height: 100%
// }}}
//Placeholder style{{{
\:-moz-placeholder
@@ -254,6 +244,11 @@ $sheet-padding: 16px
.oe_bounce_container
display: inline-block
+ // Bug lp:1051746
+ .text-tag .text-button
+ height: auto !important
+ min-height: 16px
+
// bug noted in jquery ui CSS doesn't seem to occur in IE9,
// so remove position:relative
.ui-tabs
@@ -1886,6 +1881,64 @@ $sheet-padding: 16px
@include box-sizing(border)
&:hover .oe_form_field_image_controls
display: block
+ .oe_fileupload
+ display: inline-block
+ clear: both
+ width: 100%
+ .oe_add
+ float: left
+ position: relative
+ width: 100%
+ left: +2px
+ top: +7px
+ button
+ display: inline
+ height: 24px
+ font-size: 12px
+ line-height: 12px
+ vertical-align: middle
+ button.oe_attach
+ width: 24px
+ overflow: hidden
+ width: 24px
+ overflow: hidden
+ background: transparent
+ color: #7C7BAD
+ box-shadow: none
+ border: none
+ text-shadow: none
+ .oe_e
+ position: relative
+ top: -1px
+ left: -9px
+ input.oe_form_binary_file
+ display: inline-block
+ margin-left: -5px
+ height: 28px
+ width: 52px
+ margin-top: -26px
+ .oe_attach_label
+ color: #7C7BAD
+ margin-left: -3px
+ .oe_attachments
+ margin-bottom: 4px
+ margin-right: 0px
+ font-size: 12px
+ border-radius: 2px
+ border: solid 1px rgba(124,123,173,0.14)
+ .oe_attachment
+ padding: 2px
+ padding-left: 4px
+ padding-right: 4px
+ .oe_e
+ font-size: 23px
+ margin-top: -5px
+ .oe_e:hover
+ text-decoration: none
+ .oe_attachment:nth-child(odd)
+ background: white
+ .oe_attachment:nth-child(even)
+ background: #F4F5FA
// }}}
// FormView.many2one {{{
.oe_form_field_many2one
@@ -2288,67 +2341,6 @@ $sheet-padding: 16px
float: right
color: #333
// }}}
-
-.openerp
- .oe_fileupload
- display: inline-block
- clear: both
- width: 100%
- .oe_add
- float: left
- position: relative
- width: 100%
- left: +2px
- top: +7px
- button
- display: inline
- height: 24px
- font-size: 12px
- line-height: 12px
- vertical-align: middle
- button.oe_attach
- width: 24px
- overflow: hidden
- width: 24px
- overflow: hidden
- background: transparent
- color: #7C7BAD
- box-shadow: none
- border: none
- text-shadow: none
- .oe_e
- position: relative
- top: -1px
- left: -9px
- input.oe_form_binary_file
- display: inline-block
- margin-left: -5px
- height: 28px
- width: 52px
- margin-top: -26px
- .oe_attach_label
- color: #7C7BAD
- margin-left: -3px
- .oe_attachments
- margin-bottom: 4px
- margin-right: 0px
- font-size: 12px
- border-radius: 2px
- border: solid 1px rgba(124,123,173,0.14)
- .oe_attachment
- padding: 2px
- padding-left: 4px
- padding-right: 4px
- .oe_e
- font-size: 23px
- margin-top: -5px
- .oe_e:hover
- text-decoration: none
- .oe_attachment:nth-child(odd)
- background: white
- .oe_attachment:nth-child(even)
- background: #F4F5FA
-
// Kitten Mode {{{
.kitten-mode-activated
background-image: url(http://placekitten.com/g/1365/769)
@@ -2358,11 +2350,13 @@ $sheet-padding: 16px
opacity: 0.70
// }}}
+// jQueryUI top level {{{
// The jQuery-ui overlay and Autocomplete are outside the .openerp div, please don't add indentation !!!
div.ui-widget-overlay
background: black
@include opacity(0.3)
-
+// TODO: I think only the overlay is problematic, the other top level widgets should use $.fn.openerpClass()
+// eg: $el.autocomplete().openerpClass();
.ui-widget
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
color: #4c4c4c
@@ -2389,11 +2383,14 @@ div.ui-widget-overlay
.ui-corner-all
@include radius(3px)
+// }}}
+// @media print {{{
@media print
.openerp
- .oe_header_row, ul.oe_header, div.oe_mail_thread_action, .oe_mail_recthread_actions, .oe_button_box, .oe_form button, button.oe_invite, .oe_form header, .openerp .oe_notebook > li.ui-state-default
- display: none
+ .oe_header_row, ul.oe_header, div.oe_mail_thread_action, .oe_mail_recthread_actions, .oe_button_box, .oe_form button, button.oe_invite, .oe_form header, .openerp .oe_notebook > li.ui-state-default, .oe_topbar, .oe_leftbar, .oe_loading
+ // We use !important here because jQuery adds @style = display: block on elements when using $.fn.show()
+ display: none !important
.oe_list_content
button, input[type=checkbox]
visibility: hidden
@@ -2419,5 +2416,7 @@ div.ui-widget-overlay
background: none
.openerp div.oe_mail_wall
overflow: hidden !important
+// }}}
+
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers > "%:p:r.css"
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
From 2b7bfe4ecd1a1f631c197f2dea0bdc73565ba56c Mon Sep 17 00:00:00 2001
From: Fabien Meghazi
Date: Thu, 15 Nov 2012 18:51:46 +0100
Subject: [PATCH 43/43] [IMP] openerp logo img border 0 for IE
Can't merge nwi's branch right now so I do the bare minimum that hurts eyes
bzr revid: fme@openerp.com-20121115175146-5zn97xz8w3m5l5rx
---
addons/web/static/src/css/base.css | 1 +
addons/web/static/src/css/base.sass | 1 +
2 files changed, 2 insertions(+)
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index f530415740b..ab7b74f85fe 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -1129,6 +1129,7 @@
height: 40px;
width: 157px;
margin: 14px 0;
+ border: 0;
}
.openerp .oe_footer {
position: fixed;
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index 7669af0ee2c..c5fcb8008a5 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -923,6 +923,7 @@ $sheet-padding: 16px
height: 40px
width: 157px
margin: 14px 0
+ border: 0
.oe_footer
position: fixed
bottom: 0