[MERGE]upstream
bzr revid: dle@openerp.com-20130614122854-le6s0u679k8kn547
This commit is contained in:
@@ -65,12 +65,11 @@ class bank(osv.osv):
|
||||
# Find the code and parent of the bank account to create
|
||||
dig = 6
|
||||
current_num = 1
|
||||
ids = obj_acc.search(cr, uid, [('type','=','liquidity'), ('company_id', '=', bank.company_id.id)], context=context)
|
||||
ids = obj_acc.search(cr, uid, [('type','=','liquidity'), ('company_id', '=', bank.company_id.id), ('parent_id', '!=', False)], context=context)
|
||||
# No liquidity account exists, no template available
|
||||
if not ids: continue
|
||||
|
||||
ref_acc_bank_temp = obj_acc.browse(cr, uid, ids[0], context=context)
|
||||
ref_acc_bank = ref_acc_bank_temp.parent_id
|
||||
ref_acc_bank = obj_acc.browse(cr, uid, ids[0], context=context).parent_id
|
||||
while True:
|
||||
new_code = str(ref_acc_bank.code.ljust(dig-len(str(current_num)), '0')) + str(current_num)
|
||||
ids = obj_acc.search(cr, uid, [('code', '=', new_code), ('company_id', '=', bank.company_id.id)])
|
||||
@@ -82,7 +81,7 @@ class bank(osv.osv):
|
||||
'name': name,
|
||||
'code': new_code,
|
||||
'type': 'liquidity',
|
||||
'user_type': ref_acc_bank_temp.user_type.id,
|
||||
'user_type': ref_acc_bank.user_type.id,
|
||||
'reconcile': False,
|
||||
'parent_id': ref_acc_bank.id,
|
||||
'company_id': bank.company_id.id,
|
||||
|
||||
@@ -51,9 +51,12 @@ class account_invoice(osv.osv):
|
||||
company_id = context.get('company_id', user.company_id.id)
|
||||
type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale_refund', 'in_refund': 'purchase_refund'}
|
||||
journal_obj = self.pool.get('account.journal')
|
||||
res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale')),
|
||||
('company_id', '=', company_id)],
|
||||
limit=1)
|
||||
domain = [('company_id', '=', company_id)]
|
||||
if isinstance(type_inv, list):
|
||||
domain.append(('type', 'in', [type2journal.get(type) for type in type_inv if type2journal.get(type)]))
|
||||
else:
|
||||
domain.append(('type', '=', type2journal.get(type_inv, 'sale')))
|
||||
res = journal_obj.search(cr, uid, domain, limit=1)
|
||||
return res and res[0] or False
|
||||
|
||||
def _get_currency(self, cr, uid, context=None):
|
||||
@@ -578,6 +581,10 @@ class account_invoice(osv.osv):
|
||||
return {'value': {}}
|
||||
|
||||
def onchange_company_id(self, cr, uid, ids, company_id, part_id, type, invoice_line, currency_id, context=None):
|
||||
#TODO: add the missing context parameter when forward-porting in trunk so we can remove
|
||||
# this hack!
|
||||
context = self.pool['res.users'].context_get(cr, uid)
|
||||
|
||||
val = {}
|
||||
dom = {}
|
||||
obj_journal = self.pool.get('account.journal')
|
||||
@@ -634,14 +641,13 @@ class account_invoice(osv.osv):
|
||||
else:
|
||||
continue
|
||||
if company_id and type:
|
||||
if type in ('out_invoice'):
|
||||
journal_type = 'sale'
|
||||
elif type in ('out_refund'):
|
||||
journal_type = 'sale_refund'
|
||||
elif type in ('in_refund'):
|
||||
journal_type = 'purchase_refund'
|
||||
else:
|
||||
journal_type = 'purchase'
|
||||
journal_mapping = {
|
||||
'out_invoice': 'sale',
|
||||
'out_refund': 'sale_refund',
|
||||
'in_refund': 'purchase_refund',
|
||||
'in_invoice': 'purchase',
|
||||
}
|
||||
journal_type = journal_mapping[type]
|
||||
journal_ids = obj_journal.search(cr, uid, [('company_id','=',company_id), ('type', '=', journal_type)])
|
||||
if journal_ids:
|
||||
val['journal_id'] = journal_ids[0]
|
||||
@@ -651,7 +657,12 @@ class account_invoice(osv.osv):
|
||||
if r[1] == 'journal_id' and r[2] in journal_ids:
|
||||
val['journal_id'] = r[2]
|
||||
if not val.get('journal_id', False):
|
||||
raise osv.except_osv(_('Configuration Error!'), (_('Cannot find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Journals\Journals.') % (journal_type)))
|
||||
journal_type_map = dict(obj_journal._columns['type'].selection)
|
||||
journal_type_label = self.pool['ir.translation']._get_source(cr, uid, None, ('code','selection'),
|
||||
context.get('lang'),
|
||||
journal_type_map.get(journal_type))
|
||||
raise osv.except_osv(_('Configuration Error!'),
|
||||
_('Cannot find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Journals\Journals.') % ('"%s"' % journal_type_label))
|
||||
dom = {'journal_id': [('id', 'in', journal_ids)]}
|
||||
else:
|
||||
journal_ids = obj_journal.search(cr, uid, [])
|
||||
@@ -968,7 +979,7 @@ class account_invoice(osv.osv):
|
||||
total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml, context=ctx)
|
||||
acc_id = inv.account_id.id
|
||||
|
||||
name = inv['name'] or '/'
|
||||
name = inv['name'] or inv['supplier_invoice_number'] or '/'
|
||||
totlines = False
|
||||
if inv.payment_term:
|
||||
totlines = payment_term_obj.compute(cr,
|
||||
@@ -1167,12 +1178,12 @@ class account_invoice(osv.osv):
|
||||
if not ids:
|
||||
return []
|
||||
types = {
|
||||
'out_invoice': 'Invoice ',
|
||||
'in_invoice': 'Sup. Invoice ',
|
||||
'out_refund': 'Refund ',
|
||||
'in_refund': 'Supplier Refund ',
|
||||
'out_invoice': _('Invoice'),
|
||||
'in_invoice': _('Supplier Invoice'),
|
||||
'out_refund': _('Refund'),
|
||||
'in_refund': _('Supplier Refund'),
|
||||
}
|
||||
return [(r['id'], (r['number']) or types[r['type']] + (r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')]
|
||||
return [(r['id'], '%s %s' % (r['number'] or types[r['type']], r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')]
|
||||
|
||||
def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
|
||||
if not args:
|
||||
|
||||
@@ -1066,12 +1066,12 @@ class account_move_line(osv.osv):
|
||||
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
ctx = context.copy()
|
||||
if ('journal_id' not in ctx):
|
||||
if not ctx.get('journal_id'):
|
||||
if line.move_id:
|
||||
ctx['journal_id'] = line.move_id.journal_id.id
|
||||
else:
|
||||
ctx['journal_id'] = line.journal_id.id
|
||||
if ('period_id' not in ctx):
|
||||
if not ctx.get('period_id'):
|
||||
if line.move_id:
|
||||
ctx['period_id'] = line.move_id.period_id.id
|
||||
else:
|
||||
|
||||
@@ -585,7 +585,10 @@
|
||||
<field name="date"/>
|
||||
<field name="name"/>
|
||||
<field name="ref"/>
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id)" domain="['|',('parent_id','=',False),('is_company','=',True)]"/>
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id)" domain="[
|
||||
'&',
|
||||
'|',('parent_id','=',False),('is_company','=',True),
|
||||
'|',('customer','=',True),('supplier','=',True)]"/>
|
||||
<field name="type" on_change="onchange_type(partner_id, type)"/>
|
||||
<field name="account_id" options='{"no_open":True}' domain="[('journal_id','=',parent.journal_id), ('company_id', '=', parent.company_id)]"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('company_id', '=', parent.company_id), ('type', '<>', 'view')]"/>
|
||||
|
||||
@@ -363,7 +363,7 @@
|
||||
<field name="inherit_id" ref="account.view_account_journal_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="type" position="after">
|
||||
<field name="analytic_journal_id"/>
|
||||
<field name="analytic_journal_id" groups="analytic.group_analytic_accounting"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -239,7 +239,7 @@
|
||||
<td><para style="terp_default_8">[[ line.account_id.code ]]</para></td>
|
||||
<td><para style="terp_default_8">[[ line.partner_id and strip_name(line.partner_id.name,15) ]]</para></td>
|
||||
<td><para style="terp_default_8">[[ strip_name(line.name,25) ]]</para></td>
|
||||
<td><para style="P8">[[ line.tax_code_id and (line.tax_code_id.code + ':') ]]</para></td>
|
||||
<td><para style="P8">[[ line.tax_code_id and line.tax_code_id.code and (line.tax_code_id.code + ':') ]]</para></td>
|
||||
<td><para style="terp_default_8">[[ line.tax_amount and formatLang(line.tax_amount, currency_obj=company.currency_id) ]]</para></td>
|
||||
<td><para style="P8">[[ formatLang(line.debit, currency_obj=company.currency_id) ]]</para></td>
|
||||
<td><para style="P8">[[ formatLang(line.credit, currency_obj=company.currency_id) ]]</para></td>
|
||||
@@ -292,7 +292,7 @@
|
||||
<td><para style="terp_default_8">[[ line.account_id.code ]]</para></td>
|
||||
<td><para style="terp_default_8">[[ line.partner_id and strip_name(line.partner_id.name,12) ]]</para></td>
|
||||
<td><para style="terp_default_8">[[ strip_name(line.name,16) ]]</para></td>
|
||||
<td><para style="terp_default_8">[[ line.tax_code_id and (line.tax_code_id.code + ':') ]]</para></td>
|
||||
<td><para style="terp_default_8">[[ line.tax_code_id and line.tax_code_id.code and (line.tax_code_id.code + ':') ]]</para></td>
|
||||
<td><para style="P8">[[ line.tax_amount and formatLang(line.tax_amount, currency_obj=company.currency_id) ]]</para></td>
|
||||
<td><para style="P8">[[ formatLang(line.debit, currency_obj=company.currency_id) ]]</para></td>
|
||||
<td><para style="P8">[[ formatLang(line.credit, currency_obj=company.currency_id) ]]</para></td>
|
||||
|
||||
@@ -38,7 +38,7 @@ class account_fiscalyear_close(osv.osv_memory):
|
||||
'report_name': fields.char('Name of new entries',size=64, required=True, help="Give name of the new entries"),
|
||||
}
|
||||
_defaults = {
|
||||
'report_name': _('End of Fiscal Year Entry'),
|
||||
'report_name': lambda self, cr, uid, context: _('End of Fiscal Year Entry'),
|
||||
}
|
||||
|
||||
def data_save(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -72,8 +72,8 @@ class account_invoice_line(osv.osv):
|
||||
_inherit = "account.invoice.line"
|
||||
_description = "Invoice Line"
|
||||
|
||||
def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
|
||||
res_prod = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit, currency_id=currency_id, context=context, company_id=company_id)
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
|
||||
res_prod = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id=currency_id, context=context, company_id=company_id)
|
||||
rec = self.pool.get('account.analytic.default').account_get(cr, uid, product, partner_id, uid, time.strftime('%Y-%m-%d'), context=context)
|
||||
if rec:
|
||||
res_prod['value'].update({'account_analytic_id': rec.analytic_id.id})
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<field name="inherit_id" ref="account.view_account_journal_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="centralisation" position="before">
|
||||
<field name="plan_id" />
|
||||
<field name="plan_id" groups="analytic.group_analytic_accounting"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -136,9 +136,9 @@ class account_invoice_line(osv.osv):
|
||||
res += diff_res
|
||||
return res
|
||||
|
||||
def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
|
||||
fiscal_pool = self.pool.get('account.fiscal.position')
|
||||
res = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit, currency_id, context, company_id)
|
||||
res = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id, context, company_id)
|
||||
if not product:
|
||||
return res
|
||||
if type in ('in_invoice','in_refund'):
|
||||
|
||||
@@ -13,6 +13,17 @@
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_invoice_asset_category">
|
||||
<field name="name">account.invoice.supplier.form</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='invoice_line']/tree/field[@name='quantity']" position="before">
|
||||
<field name="asset_category_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -203,7 +203,9 @@
|
||||
<field name="view_id" ref="crossovered_budget_view_tree"/>
|
||||
<field name="search_view_id" ref="view_crossovered_budget_search"/>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new budget.
|
||||
</p><p>
|
||||
A budget is a forecast of your company's income and/or expenses
|
||||
expected for a period in the future. A budget is defined on some
|
||||
financial accounts and/or analytic accounts (that may represent
|
||||
|
||||
@@ -3,5 +3,5 @@ access_account_followup_followup_line,account_followup.followup.line,model_accou
|
||||
access_account_followup_followup_line_manager,account_followup.followup.line.manager,model_account_followup_followup_line,account.group_account_manager,1,1,1,1
|
||||
access_account_followup_followup_accountant,account_followup.followup user,model_account_followup_followup,account.group_account_invoice,1,0,0,0
|
||||
access_account_followup_followup_manager,account_followup.followup.manager,model_account_followup_followup,account.group_account_manager,1,1,1,1
|
||||
access_account_followup_stat_invoice,account_followup.stat.invoice,model_account_followup_stat,account.group_account_invoice,1,1,1,1
|
||||
access_account_followup_stat_by_partner_manager,account_followup.stat.by.partner,model_account_followup_stat_by_partner,account.group_account_user,1,1,1,1
|
||||
access_account_followup_stat_invoice,account_followup.stat.invoice,model_account_followup_stat,account.group_account_invoice,1,1,0,0
|
||||
access_account_followup_stat_by_partner_manager,account_followup.stat.by.partner,model_account_followup_stat_by_partner,account.group_account_user,1,1,0,0
|
||||
|
||||
|
@@ -88,6 +88,7 @@ class payment_order_create(osv.osv_memory):
|
||||
'order_id': payment.id,
|
||||
'partner_id': line.partner_id and line.partner_id.id or False,
|
||||
'communication': line.ref or '/',
|
||||
'state': line.invoice and line.invoice.reference_type != 'none' and 'structured' or 'normal',
|
||||
'date': date_to_pay,
|
||||
'currency': (line.invoice and line.invoice.currency_id.id) or line.journal_id.currency.id or line.journal_id.company_id.currency_id.id,
|
||||
}, context=context)
|
||||
|
||||
@@ -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-12-21 17:04+0000\n"
|
||||
"PO-Revision-Date: 2013-05-29 13:17+0000\n"
|
||||
"PO-Revision-Date: 2013-06-13 11:30+0000\n"
|
||||
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2013-05-30 05:45+0000\n"
|
||||
"X-Generator: Launchpad (build 16652)\n"
|
||||
"X-Launchpad-Export-Date: 2013-06-14 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 16667)\n"
|
||||
|
||||
#. module: account_voucher
|
||||
#: field:account.bank.statement.line,voucher_id:0
|
||||
@@ -27,7 +27,7 @@ msgid "account.config.settings"
|
||||
msgstr "account.config.settings"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:369
|
||||
#: code:addons/account_voucher/account_voucher.py:417
|
||||
#, python-format
|
||||
msgid "Write-Off"
|
||||
msgstr "Списание"
|
||||
@@ -129,7 +129,7 @@ msgid "Voucher Statistics"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1547
|
||||
#: code:addons/account_voucher/account_voucher.py:1641
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can not change the journal as you already reconciled some statement "
|
||||
@@ -223,8 +223,8 @@ msgid "Journal Item"
|
||||
msgstr "Элемент журнала"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:508
|
||||
#: code:addons/account_voucher/account_voucher.py:981
|
||||
#: code:addons/account_voucher/account_voucher.py:558
|
||||
#: code:addons/account_voucher/account_voucher.py:1073
|
||||
#, python-format
|
||||
msgid "Error!"
|
||||
msgstr "Ошибка!"
|
||||
@@ -251,7 +251,7 @@ msgid "Cancelled"
|
||||
msgstr "Отменено"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1153
|
||||
#: code:addons/account_voucher/account_voucher.py:1249
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You have to configure account base code and account tax code on the '%s' tax!"
|
||||
@@ -295,7 +295,7 @@ msgid "Tax"
|
||||
msgstr "Налог"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:879
|
||||
#: code:addons/account_voucher/account_voucher.py:971
|
||||
#, python-format
|
||||
msgid "Invalid Action!"
|
||||
msgstr "Неверное действие!"
|
||||
@@ -348,7 +348,7 @@ msgid "Import Invoices"
|
||||
msgstr "Импорт счетов"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1112
|
||||
#: code:addons/account_voucher/account_voucher.py:1208
|
||||
#, python-format
|
||||
msgid "Wrong voucher line"
|
||||
msgstr ""
|
||||
@@ -367,7 +367,7 @@ msgid "Receipt"
|
||||
msgstr "Приход"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1018
|
||||
#: code:addons/account_voucher/account_voucher.py:1110
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You should configure the 'Gain Exchange Rate Account' in the accounting "
|
||||
@@ -388,7 +388,7 @@ msgstr "Период"
|
||||
|
||||
#. module: account_voucher
|
||||
#: view:account.voucher:0
|
||||
#: code:addons/account_voucher/account_voucher.py:211
|
||||
#: code:addons/account_voucher/account_voucher.py:231
|
||||
#, python-format
|
||||
msgid "Supplier"
|
||||
msgstr "Поставщик"
|
||||
@@ -409,7 +409,7 @@ msgid "Debit"
|
||||
msgstr "Дебет"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1547
|
||||
#: code:addons/account_voucher/account_voucher.py:1641
|
||||
#, python-format
|
||||
msgid "Unable to change journal !"
|
||||
msgstr "Невозможно изменить журнал!"
|
||||
@@ -515,7 +515,7 @@ msgid "Pay Invoice"
|
||||
msgstr "Оплата счета"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1153
|
||||
#: code:addons/account_voucher/account_voucher.py:1249
|
||||
#, python-format
|
||||
msgid "No Account Base Code and Account Tax Code!"
|
||||
msgstr ""
|
||||
@@ -569,15 +569,15 @@ msgid "To Review"
|
||||
msgstr "Для проверки"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1025
|
||||
#: code:addons/account_voucher/account_voucher.py:1039
|
||||
#: code:addons/account_voucher/account_voucher.py:1194
|
||||
#: code:addons/account_voucher/account_voucher.py:1120
|
||||
#: code:addons/account_voucher/account_voucher.py:1134
|
||||
#: code:addons/account_voucher/account_voucher.py:1286
|
||||
#, python-format
|
||||
msgid "change"
|
||||
msgstr "изменить"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1014
|
||||
#: code:addons/account_voucher/account_voucher.py:1106
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You should configure the 'Loss Exchange Rate Account' in the accounting "
|
||||
@@ -631,6 +631,7 @@ msgstr "Месяц"
|
||||
#. module: account_voucher
|
||||
#: field:account.voucher,currency_id:0
|
||||
#: field:account.voucher.line,currency_id:0
|
||||
#: model:ir.model,name:account_voucher.model_res_currency
|
||||
#: field:sale.receipt.report,currency_id:0
|
||||
msgid "Currency"
|
||||
msgstr "Валюта"
|
||||
@@ -674,7 +675,7 @@ msgid "Reconcile Payment Balance"
|
||||
msgstr "Сверка платежного баланса"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:975
|
||||
#: code:addons/account_voucher/account_voucher.py:1067
|
||||
#, python-format
|
||||
msgid "Configuration Error !"
|
||||
msgstr "Ошибка конфигурации !"
|
||||
@@ -739,7 +740,7 @@ msgid "October"
|
||||
msgstr "Октябрь"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:976
|
||||
#: code:addons/account_voucher/account_voucher.py:1068
|
||||
#, python-format
|
||||
msgid "Please activate the sequence of selected journal !"
|
||||
msgstr "Пожалуйста, включите нумерацию выбранного журнала!"
|
||||
@@ -819,7 +820,7 @@ msgid "Previous Payments ?"
|
||||
msgstr "Предыдущие платежи ?"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1112
|
||||
#: code:addons/account_voucher/account_voucher.py:1208
|
||||
#, python-format
|
||||
msgid "The invoice you are willing to pay is not valid anymore."
|
||||
msgstr "Счет, который вы готовы платить, уже не актуален ."
|
||||
@@ -851,7 +852,7 @@ msgid "Active"
|
||||
msgstr "Активно"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:982
|
||||
#: code:addons/account_voucher/account_voucher.py:1074
|
||||
#, python-format
|
||||
msgid "Please define a sequence on the journal."
|
||||
msgstr "Пожалуйста, определите нумерацию журнала."
|
||||
@@ -861,7 +862,7 @@ msgstr "Пожалуйста, определите нумерацию журна
|
||||
#: model:ir.actions.act_window,name:account_voucher.action_vendor_receipt
|
||||
#: model:ir.ui.menu,name:account_voucher.menu_action_vendor_receipt
|
||||
msgid "Customer Payments"
|
||||
msgstr "Платежи клиентов"
|
||||
msgstr "Платежи заказчиков"
|
||||
|
||||
#. module: account_voucher
|
||||
#: model:ir.actions.act_window,name:account_voucher.action_sale_receipt_report_all
|
||||
@@ -979,7 +980,7 @@ msgid "Journal Items"
|
||||
msgstr "Элементы журнала"
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:508
|
||||
#: code:addons/account_voucher/account_voucher.py:558
|
||||
#, python-format
|
||||
msgid "Please define default credit/debit accounts on the journal \"%s\"."
|
||||
msgstr ""
|
||||
@@ -1188,7 +1189,7 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:879
|
||||
#: code:addons/account_voucher/account_voucher.py:971
|
||||
#, python-format
|
||||
msgid "Cannot delete voucher(s) which are already opened or paid."
|
||||
msgstr ""
|
||||
@@ -1251,8 +1252,8 @@ msgid "Status <b>changed</b>"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_voucher
|
||||
#: code:addons/account_voucher/account_voucher.py:1014
|
||||
#: code:addons/account_voucher/account_voucher.py:1018
|
||||
#: code:addons/account_voucher/account_voucher.py:1106
|
||||
#: code:addons/account_voucher/account_voucher.py:1110
|
||||
#, python-format
|
||||
msgid "Insufficient Configuration!"
|
||||
msgstr ""
|
||||
|
||||
@@ -102,7 +102,10 @@ class OAuthController(oeweb.Controller):
|
||||
registry = RegistryManager.get(dbname)
|
||||
with registry.cursor() as cr:
|
||||
IMD = registry['ir.model.data']
|
||||
model, provider_id = IMD.get_object_reference(cr, SUPERUSER_ID, 'auth_oauth', 'provider_openerp')
|
||||
try:
|
||||
model, provider_id = IMD.get_object_reference(cr, SUPERUSER_ID, 'auth_oauth', 'provider_openerp')
|
||||
except ValueError:
|
||||
return set_cookie_and_redirect(req, '/?db=%s' % dbname)
|
||||
assert model == 'auth.oauth.provider'
|
||||
|
||||
state = {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
Name,Is a company,Related company,Address type,Customer,Supplier,Street,ZIP,City,State,Country
|
||||
Aurora Shelves,1,,,1,0,25 Pacific Road,95101,San José,CA,United States
|
||||
Roger Martins,0,Aurora Shelves,Invoice,1,0,27 Pacific Road,95102,San José,CA,United States
|
||||
House Sales Direct,1,,,1,0,104 Saint Mary Avenue,94059,Redwood,CA,United States
|
||||
Yvan Holiday,0,House Sales Direct,Default,1,0,104 Saint Mary Avenue,94060,Redwood,CA,United States
|
||||
Jack Unsworth,0,House Sales Direct,Invoice,1,0,227 Jackson Road,94061,Redwood,CA,United States
|
||||
Michael Mason,0,,,1,0,16 5th Avenue,94104,San Francisco,CA,United States
|
||||
International Wood,1,,,1,0,748 White House Boulevard,20004,Washington,DC,United States
|
||||
Sharon Pecker,0,International Wood,Invoice,1,0,755 White House Boulevard,20005,Washington,DC,United States
|
||||
|
@@ -1,8 +0,0 @@
|
||||
Name,Address type,Street,City,Country,Tags,Supplier,Customer,Is a company,Companies that refers to partner / Parent company
|
||||
Wood y Wood Pecker,,"Snow Street, 25",Kainuu,Finland,Supplier,1,0,1,
|
||||
Roger Pecker,Default,"Snow Street, 27",Kainuu,Finland,Supplier,1,0,0,Wood y Wood Pecker
|
||||
Sharon Pecker,Delivery,"Snow Street, 28",Kainuu,Finland,Supplier,1,0,0,Wood y Wood Pecker
|
||||
Thomas Pecker,Contact,"Snow Street, 27",Kainuu,Finland,Supplier,1,0,0,Wood y Wood Pecker
|
||||
Vicking Direct,,"Atonium Street, 45a",Brussels,Belgium,Supplier,1,0,1,
|
||||
Yvan Holiday,Invoice,"Atonium Street, 45b",Brussels,Belgium,Supplier,1,0,0,Vicking Direct
|
||||
Jack Unsworth,Contact,"Atonium Street, 45a",Brussels,Belgium,Supplier,1,0,0,Vicking Direct
|
||||
|
@@ -229,8 +229,8 @@
|
||||
orders with their respective purchase order lines:</p>
|
||||
<a href="/base_import/static/csv/o2m_purchase_order_lines.csv">Purchase orders with their respective purchase order lines</a>
|
||||
<p>The following CSV file shows how to import
|
||||
suppliers and their respective contacts</p>
|
||||
<a href="/base_import/static/csv/o2m_suppliers_contacts.csv">Suppliers and their respective contacts</a>
|
||||
customers and their respective contacts</p>
|
||||
<a href="/base_import/static/csv/o2m_customers_contacts.csv">Customers and their respective contacts</a>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
@@ -14,9 +14,13 @@
|
||||
<label string="This plug-in allows you to create/modify OpenERP Reports into OpenOffice Writer."/>
|
||||
</separator>
|
||||
<xpath expr="//button[@string='Install Modules']" position="replace">
|
||||
<button name="action_next" icon="gtk-go-forward" type="object" string="Configure" invisible="context.get('menu',False)" class="oe_highlight"/>
|
||||
<button name="action_next" type="object" string="Configure" invisible="context.get('menu',False)" class="oe_highlight"/>
|
||||
<label string="or" invisible="context.get('menu',False)" class="oe_inline"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@string='Skip']" position="replace">
|
||||
<button string="Ok" class="oe_highlight" special="cancel" invisible="context.get('active_model',False)"/>
|
||||
<button string="Skip" class="oe_link" special="cancel" invisible="context.get('menu',False)"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//separator[@string='title']" position="after">
|
||||
<group colspan="8" height="450" width="750">
|
||||
<field name="name" invisible="1"/>
|
||||
|
||||
@@ -61,8 +61,8 @@ class RPCSession(object):
|
||||
protocol = m.group(1)
|
||||
if not m:
|
||||
return -1
|
||||
if protocol == 'http://' or protocol == 'http://':
|
||||
self.gateway = XMLRPCGateway(host, port, 'http')
|
||||
if protocol == 'http://' or protocol == 'https://':
|
||||
self.gateway = XMLRPCGateway(host, port, protocol[:-3])
|
||||
elif protocol == 'socket://':
|
||||
|
||||
self.gateway = NETRPCGateway(host, port)
|
||||
|
||||
BIN
Binary file not shown.
@@ -344,7 +344,7 @@ instance.board.AddToDashboard = instance.web.search.Input.extend({
|
||||
},
|
||||
load_data:function(){
|
||||
var board = new instance.web.Model('board.board');
|
||||
return board.call('list');
|
||||
return board.call('list', [board.context()]);
|
||||
},
|
||||
_x:function() {
|
||||
if (!instance.webclient) { return $.Deferred().reject(); }
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
<field name="view_id" ref="crm_case_claims_tree_view"/>
|
||||
<field name="context">{"search_default_user_id":uid, "stage_type":'claim'}</field>
|
||||
<field name="search_view_id" ref="crm_claim.view_crm_case_claims_filter"/>
|
||||
<field name="help">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.</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
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.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_claim0">
|
||||
|
||||
@@ -88,7 +88,11 @@
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="view_wiki_tree"/>
|
||||
<field name="search_view_id" ref="view_wiki_filter"/>
|
||||
<field name="help">Create web pages</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new web page.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem id="menu_page" parent="menu_wiki" name="Pages" action="action_page" sequence="10"/>
|
||||
<record id="action_category" model="ir.actions.act_window">
|
||||
|
||||
@@ -24,8 +24,8 @@ import base64
|
||||
import logging
|
||||
|
||||
import openerp
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.osv import fields
|
||||
from openerp import tools
|
||||
from openerp.tools.translate import _
|
||||
from urllib import urlencode, quote as quote
|
||||
@@ -191,7 +191,6 @@ class email_template(osv.osv):
|
||||
}
|
||||
|
||||
def create_action(self, cr, uid, ids, context=None):
|
||||
vals = {}
|
||||
action_obj = self.pool.get('ir.actions.act_window')
|
||||
data_obj = self.pool.get('ir.model.data')
|
||||
for template in self.browse(cr, uid, ids, context=context):
|
||||
@@ -199,7 +198,7 @@ class email_template(osv.osv):
|
||||
model_data_id = data_obj._get_id(cr, uid, 'mail', 'email_compose_message_wizard_form')
|
||||
res_id = data_obj.browse(cr, uid, model_data_id, context=context).res_id
|
||||
button_name = _('Send Mail (%s)') % template.name
|
||||
vals['ref_ir_act_window'] = action_obj.create(cr, uid, {
|
||||
act_id = action_obj.create(cr, SUPERUSER_ID, {
|
||||
'name': button_name,
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'mail.compose.message',
|
||||
@@ -211,27 +210,29 @@ class email_template(osv.osv):
|
||||
'target': 'new',
|
||||
'auto_refresh':1
|
||||
}, context)
|
||||
vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, uid, {
|
||||
ir_values_id = self.pool.get('ir.values').create(cr, SUPERUSER_ID, {
|
||||
'name': button_name,
|
||||
'model': src_obj,
|
||||
'key2': 'client_action_multi',
|
||||
'value': "ir.actions.act_window," + str(vals['ref_ir_act_window']),
|
||||
'value': "ir.actions.act_window,%s" % act_id,
|
||||
'object': True,
|
||||
}, context)
|
||||
self.write(cr, uid, ids, {
|
||||
'ref_ir_act_window': vals.get('ref_ir_act_window',False),
|
||||
'ref_ir_value': vals.get('ref_ir_value',False),
|
||||
}, context)
|
||||
|
||||
template.write({
|
||||
'ref_ir_act_window': act_id,
|
||||
'ref_ir_value': ir_values_id,
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
def unlink_action(self, cr, uid, ids, context=None):
|
||||
for template in self.browse(cr, uid, ids, context=context):
|
||||
try:
|
||||
if template.ref_ir_act_window:
|
||||
self.pool.get('ir.actions.act_window').unlink(cr, uid, template.ref_ir_act_window.id, context)
|
||||
self.pool.get('ir.actions.act_window').unlink(cr, SUPERUSER_ID, template.ref_ir_act_window.id, context)
|
||||
if template.ref_ir_value:
|
||||
ir_values_obj = self.pool.get('ir.values')
|
||||
ir_values_obj.unlink(cr, uid, template.ref_ir_value.id, context)
|
||||
ir_values_obj.unlink(cr, SUPERUSER_ID, template.ref_ir_value.id, context)
|
||||
except Exception:
|
||||
raise osv.except_osv(_("Warning"), _("Deletion of the action record failed."))
|
||||
return True
|
||||
|
||||
@@ -54,18 +54,22 @@ class mail_compose_message(osv.TransientModel):
|
||||
Indeed, basic mail.compose.message wizard duplicates attachments in mass
|
||||
mailing mode. But in 'single post' mode, attachments of an email template
|
||||
also have to be duplicated to avoid changing their ownership. """
|
||||
if context is None:
|
||||
context = {}
|
||||
wizard_context = dict(context)
|
||||
for wizard in self.browse(cr, uid, ids, context=context):
|
||||
if wizard.template_id and not wizard.template_id.user_signature:
|
||||
wizard_context['mail_notify_user_signature'] = False # template user_signature is added when generating body_html
|
||||
if not wizard.attachment_ids or wizard.composition_mode == 'mass_mail' or not wizard.template_id:
|
||||
continue
|
||||
template = self.pool.get('email.template').browse(cr, uid, wizard.template_id.id, context=context)
|
||||
new_attachment_ids = []
|
||||
for attachment in wizard.attachment_ids:
|
||||
if attachment in template.attachment_ids:
|
||||
if attachment in wizard.template_id.attachment_ids:
|
||||
new_attachment_ids.append(self.pool.get('ir.attachment').copy(cr, uid, attachment.id, {'res_model': 'mail.compose.message', 'res_id': wizard.id}, context=context))
|
||||
else:
|
||||
new_attachment_ids.append(attachment.id)
|
||||
self.write(cr, uid, wizard.id, {'attachment_ids': [(6, 0, new_attachment_ids)]}, context=context)
|
||||
return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
|
||||
return super(mail_compose_message, self).send_mail(cr, uid, ids, context=wizard_context)
|
||||
|
||||
def onchange_template_id(self, cr, uid, ids, template_id, composition_mode, model, res_id, context=None):
|
||||
""" - mass_mailing: we cannot render, so return the template values
|
||||
|
||||
@@ -591,6 +591,9 @@
|
||||
<field name="view_mode">tree,graph</field>
|
||||
<field name="context">{"search_default_groupby_vehicle" : True}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new odometer log.
|
||||
</p>
|
||||
<p>
|
||||
Here you can add various odometer entries for all vehicles.
|
||||
You can also show odometer value for a particular vehicle using
|
||||
|
||||
@@ -23,5 +23,6 @@ import hr_department
|
||||
import hr
|
||||
|
||||
import res_config
|
||||
import res_users
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -59,6 +59,8 @@ You can manage:
|
||||
'hr_installer.xml',
|
||||
'hr_data.xml',
|
||||
'res_config_view.xml',
|
||||
'mail_hr_view.xml',
|
||||
'res_users_view.xml',
|
||||
],
|
||||
'demo': ['hr_demo.xml'],
|
||||
'test': [
|
||||
@@ -69,5 +71,7 @@ You can manage:
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
'css': [ 'static/src/css/hr.css' ],
|
||||
'js': [ 'static/src/js/suggestions.js' ],
|
||||
'qweb': [ 'static/src/xml/suggestions.xml' ],
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
+81
-26
@@ -25,9 +25,11 @@ from openerp.modules.module import get_module_resource
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
from openerp import tools
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class hr_employee_category(osv.osv):
|
||||
|
||||
def name_get(self, cr, uid, ids, context=None):
|
||||
@@ -150,6 +152,7 @@ class hr_job(osv.osv):
|
||||
class hr_employee(osv.osv):
|
||||
_name = "hr.employee"
|
||||
_description = "Employee"
|
||||
_order = 'name_related'
|
||||
_inherits = {'resource.resource': "resource_id"}
|
||||
_inherit = ['mail.thread']
|
||||
|
||||
@@ -158,10 +161,10 @@ class hr_employee(osv.osv):
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
result[obj.id] = tools.image_get_resized_images(obj.image)
|
||||
return result
|
||||
|
||||
|
||||
def _set_image(self, cr, uid, id, name, value, args, context=None):
|
||||
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
|
||||
|
||||
|
||||
_columns = {
|
||||
#we need a related field in order to be able to sort the employee by name
|
||||
'name_related': fields.related('resource_id', 'name', type='char', string='Name', readonly=True, store=True),
|
||||
@@ -171,12 +174,12 @@ class hr_employee(osv.osv):
|
||||
'sinid': fields.char('SIN No', size=32, help="Social Insurance Number"),
|
||||
'identification_id': fields.char('Identification No', size=32),
|
||||
'otherid': fields.char('Other Id', size=64),
|
||||
'gender': fields.selection([('male', 'Male'),('female', 'Female')], 'Gender'),
|
||||
'gender': fields.selection([('male', 'Male'), ('female', 'Female')], 'Gender'),
|
||||
'marital': fields.selection([('single', 'Single'), ('married', 'Married'), ('widower', 'Widower'), ('divorced', 'Divorced')], 'Marital Status'),
|
||||
'department_id':fields.many2one('hr.department', 'Department'),
|
||||
'department_id': fields.many2one('hr.department', 'Department'),
|
||||
'address_id': fields.many2one('res.partner', 'Working Address'),
|
||||
'address_home_id': fields.many2one('res.partner', 'Home Address'),
|
||||
'bank_account_id':fields.many2one('res.partner.bank', 'Bank Account Number', domain="[('partner_id','=',address_home_id)]", help="Employee bank salary account"),
|
||||
'bank_account_id': fields.many2one('res.partner.bank', 'Bank Account Number', domain="[('partner_id','=',address_home_id)]", help="Employee bank salary account"),
|
||||
'work_phone': fields.char('Work Phone', size=32, readonly=False),
|
||||
'mobile_phone': fields.char('Work Mobile', size=32, readonly=False),
|
||||
'work_email': fields.char('Work Email', size=240),
|
||||
@@ -207,25 +210,42 @@ class hr_employee(osv.osv):
|
||||
help="Small-sized photo of the employee. It is automatically "\
|
||||
"resized as a 64x64px image, with aspect ratio preserved. "\
|
||||
"Use this field anywhere a small image is required."),
|
||||
'passport_id':fields.char('Passport No', size=64),
|
||||
'passport_id': fields.char('Passport No', size=64),
|
||||
'color': fields.integer('Color Index'),
|
||||
'city': fields.related('address_id', 'city', type='char', string='City'),
|
||||
'login': fields.related('user_id', 'login', type='char', string='Login', readonly=1),
|
||||
'last_login': fields.related('user_id', 'date', type='datetime', string='Latest Connection', readonly=1),
|
||||
}
|
||||
|
||||
_order='name_related'
|
||||
def _get_default_image(self, cr, uid, context=None):
|
||||
image_path = get_module_resource('hr', 'static/src/img', 'default_image.png')
|
||||
return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
|
||||
|
||||
defaults = {
|
||||
'active': 1,
|
||||
'image': _get_default_image,
|
||||
'color': 0,
|
||||
}
|
||||
|
||||
def create(self, cr, uid, data, context=None):
|
||||
employee_id = super(hr_employee, self).create(cr, uid, data, context=context)
|
||||
try:
|
||||
(model, mail_group_id) = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'group_all_employees')
|
||||
employee = self.browse(cr, uid, employee_id, context=context)
|
||||
self.pool.get('mail.group').message_post(cr, uid, [mail_group_id],
|
||||
body=_('Welcome to %s! Please help him/her take the first steps with OpenERP!') % (employee.name),
|
||||
subtype='mail.mt_comment', context=context)
|
||||
except:
|
||||
pass # group deleted: do not push a message
|
||||
if context is None:
|
||||
context = {}
|
||||
create_ctx = dict(context, mail_create_nolog=True)
|
||||
employee_id = super(hr_employee, self).create(cr, uid, data, context=create_ctx)
|
||||
employee = self.browse(cr, uid, employee_id, context=context)
|
||||
if employee.user_id:
|
||||
# send a copy to every user of the company
|
||||
company_id = employee.user_id.partner_id.company_id.id
|
||||
partner_ids = self.pool.get('res.partner').search(cr, uid, [
|
||||
('company_id', '=', company_id),
|
||||
('user_ids', '!=', False)], context=context)
|
||||
else:
|
||||
partner_ids = []
|
||||
self.message_post(cr, uid, [employee_id],
|
||||
body=_('Welcome to %s! Please help him/her take the first steps with OpenERP!') % (employee.name),
|
||||
partner_ids=partner_ids,
|
||||
subtype='mail.mt_comment', context=context
|
||||
)
|
||||
return employee_id
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
@@ -246,7 +266,7 @@ class hr_employee(osv.osv):
|
||||
company_id = self.pool.get('res.company').browse(cr, uid, company, context=context)
|
||||
address = self.pool.get('res.partner').address_get(cr, uid, [company_id.partner_id.id], ['default'])
|
||||
address_id = address and address['default'] or False
|
||||
return {'value': {'address_id' : address_id}}
|
||||
return {'value': {'address_id': address_id}}
|
||||
|
||||
def onchange_department_id(self, cr, uid, ids, department_id, context=None):
|
||||
value = {'parent_id': False}
|
||||
@@ -259,17 +279,36 @@ class hr_employee(osv.osv):
|
||||
work_email = False
|
||||
if user_id:
|
||||
work_email = self.pool.get('res.users').browse(cr, uid, user_id, context=context).email
|
||||
return {'value': {'work_email' : work_email}}
|
||||
return {'value': {'work_email': work_email}}
|
||||
|
||||
def _get_default_image(self, cr, uid, context=None):
|
||||
image_path = get_module_resource('hr', 'static/src/img', 'default_image.png')
|
||||
return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
|
||||
def action_follow(self, cr, uid, ids, context=None):
|
||||
""" Wrapper because message_subscribe_users take a user_ids=None
|
||||
that receive the context without the wrapper. """
|
||||
return self.message_subscribe_users(cr, uid, ids, context=context)
|
||||
|
||||
_defaults = {
|
||||
'active': 1,
|
||||
'image': _get_default_image,
|
||||
'color': 0,
|
||||
}
|
||||
def action_unfollow(self, cr, uid, ids, context=None):
|
||||
""" Wrapper because message_unsubscribe_users take a user_ids=None
|
||||
that receive the context without the wrapper. """
|
||||
return self.message_unsubscribe_users(cr, uid, ids, context=context)
|
||||
|
||||
def get_suggested_thread(self, cr, uid, removed_suggested_threads=None, context=None):
|
||||
"""Show the suggestion of employees if display_employees_suggestions if the
|
||||
user perference allows it. """
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||
if not user.display_employees_suggestions:
|
||||
return []
|
||||
else:
|
||||
return super(hr_employee, self).get_suggested_thread(cr, uid, removed_suggested_threads, context)
|
||||
|
||||
def _message_get_auto_subscribe_fields(self, cr, uid, updated_fields, auto_follow_fields=['user_id'], context=None):
|
||||
""" Overwrite of the original method to always follow user_id field,
|
||||
even when not track_visibility so that a user will follow it's employee
|
||||
"""
|
||||
user_field_lst = []
|
||||
for name, column_info in self._all_columns.items():
|
||||
if name in auto_follow_fields and name in updated_fields and column_info.column._obj == 'res.users':
|
||||
user_field_lst.append(name)
|
||||
return user_field_lst
|
||||
|
||||
def _check_recursion(self, cr, uid, ids, context=None):
|
||||
level = 100
|
||||
@@ -285,6 +324,22 @@ class hr_employee(osv.osv):
|
||||
(_check_recursion, 'Error! You cannot create recursive hierarchy of Employee(s).', ['parent_id']),
|
||||
]
|
||||
|
||||
# ---------------------------------------------------
|
||||
# Mail gateway
|
||||
# ---------------------------------------------------
|
||||
|
||||
def check_mail_message_access(self, cr, uid, mids, operation, model_obj=None, context=None):
|
||||
""" mail.message document permission rule: can post a new message if can read
|
||||
because of portal document. """
|
||||
if not model_obj:
|
||||
model_obj = self
|
||||
employee_ids = model_obj.search(cr, uid, [('user_id', '=', uid)], context=context)
|
||||
if employee_ids and operation == 'create':
|
||||
model_obj.check_access_rights(cr, uid, 'read')
|
||||
model_obj.check_access_rule(cr, uid, mids, 'read', context=context)
|
||||
else:
|
||||
return super(hr_employee, self).check_mail_message_access(cr, uid, mids, operation, model_obj=model_obj, context=context)
|
||||
|
||||
|
||||
class hr_department(osv.osv):
|
||||
_description = "Department"
|
||||
|
||||
+18
-4
@@ -27,6 +27,10 @@
|
||||
</h1>
|
||||
<label for="category_ids" class="oe_edit_only" groups="base.group_hr_user"/>
|
||||
<field name="category_ids" widget="many2many_tags" placeholder="e.g. Part Time" groups="base.group_hr_user"/>
|
||||
<label for="work_email" class="oe_edit_only"/>
|
||||
<field name="work_email" widget="email"/>
|
||||
<label for="work_phone" class="oe_edit_only"/>
|
||||
<field name="work_phone"/>
|
||||
</div>
|
||||
<div class="oe_right oe_button_box" name="button_box">
|
||||
<!-- Put here related buttons -->
|
||||
@@ -36,8 +40,6 @@
|
||||
<group>
|
||||
<group string="Contact Information">
|
||||
<field name="address_id" on_change="onchange_address_id(address_id)" context="{'show_address': 1}" options='{"always_reload": True, "highlight_first_line": True}'/>
|
||||
<field name="work_email" widget="email"/>
|
||||
<field name="work_phone"/>
|
||||
<field name="mobile_phone"/>
|
||||
<field name="work_location"/>
|
||||
</group>
|
||||
@@ -136,6 +138,9 @@
|
||||
<field name="arch" type="xml">
|
||||
<kanban>
|
||||
<field name="last_login"/>
|
||||
<field name="message_is_follower"/>
|
||||
<field name="message_follower_ids"/>
|
||||
<field name="message_ids"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_employee_vignette">
|
||||
@@ -154,10 +159,19 @@
|
||||
</li>
|
||||
<li t-if="record.job_id.raw_value"><field name="job_id"/></li>
|
||||
<li t-if="record.work_location.raw_value"><field name="work_location"/></li>
|
||||
<li t-if="record.work_phone.raw_value">Tel: <field name="work_phone"/></li>
|
||||
<li t-if="record.mobile_phone.raw_value">Mobile: <field name="mobile_phone"/></li>
|
||||
<li t-if="record.work_email.raw_value"><a t-attf-href="mailto:#{record.work_email.value}"><field name="work_email"/></a></li>
|
||||
</ul>
|
||||
<div class="oe_kanban_footer_left">
|
||||
<span title='Messages'><span class='oe_e'>9</span><t t-esc="record.message_ids.raw_value.length"/></span>
|
||||
<span title='Followers'><span class='oe_e'>+</span><t t-esc="record.message_follower_ids.raw_value.length"/></span>
|
||||
</div>
|
||||
<div class="oe_followers" groups="base.group_user">
|
||||
<button t-if="record.message_is_follower.raw_value" name="action_unfollow" type="object" class="oe_follower oe_following">
|
||||
<span class="oe_unfollow">Unfollow</span>
|
||||
<span class="oe_following">Following</span>
|
||||
</button>
|
||||
<button t-if="! record.message_is_follower.raw_value" name="action_follow" type="object" class="oe_follower oe_notfollow">Follow</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="mail.action_mail_inbox_feeds" model="ir.actions.client">
|
||||
<field name="params" eval=""{
|
||||
'domain': [
|
||||
('to_read', '=', True),
|
||||
('starred', '=', False),
|
||||
],
|
||||
'view_mailbox': True,
|
||||
'view_inbox': True,
|
||||
'read_action': 'read',
|
||||
'show_compose_message': True
|
||||
}""/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,65 @@
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class res_users(osv.Model):
|
||||
""" Update of res.users class
|
||||
- if adding groups to an user, check if base.group_user is in it
|
||||
(member of 'Employee'), create an employee form linked to it.
|
||||
"""
|
||||
_name = 'res.users'
|
||||
_inherit = ['res.users']
|
||||
|
||||
_columns = {
|
||||
'display_employees_suggestions': fields.boolean("Display Employees Suggestions"),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'display_employees_suggestions': True,
|
||||
}
|
||||
|
||||
def __init__(self, pool, cr):
|
||||
""" Override of __init__ to add access rights on
|
||||
display_employees_suggestions fields. Access rights are disabled by
|
||||
default, but allowed on some specific fields defined in
|
||||
self.SELF_{READ/WRITE}ABLE_FIELDS.
|
||||
"""
|
||||
init_res = super(res_users, self).__init__(pool, cr)
|
||||
# duplicate list to avoid modifying the original reference
|
||||
self.SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS)
|
||||
self.SELF_WRITEABLE_FIELDS.append('display_employees_suggestions')
|
||||
# duplicate list to avoid modifying the original reference
|
||||
self.SELF_READABLE_FIELDS = list(self.SELF_READABLE_FIELDS)
|
||||
self.SELF_READABLE_FIELDS.append('display_employees_suggestions')
|
||||
return init_res
|
||||
|
||||
def stop_showing_employees_suggestions(self, cr, uid, user_id, context=None):
|
||||
"""Update display_employees_suggestions value to False"""
|
||||
if context is None:
|
||||
context = {}
|
||||
self.write(cr, uid, user_id, {"display_employees_suggestions": False}, context)
|
||||
|
||||
def _create_welcome_message(self, cr, uid, user, context=None):
|
||||
"""Do not welcome new users anymore, welcome new employees instead"""
|
||||
return True
|
||||
|
||||
def _message_post_get_eid(self, cr, uid, thread_id, context=None):
|
||||
assert thread_id, "res.users does not support posting global messages"
|
||||
if context and 'thread_model' in context:
|
||||
context['thread_model'] = 'hr.employee'
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id[0]
|
||||
return self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', thread_id)], context=context)
|
||||
|
||||
def message_post(self, cr, uid, thread_id, context=None, **kwargs):
|
||||
""" Redirect the posting of message on res.users to the related employee.
|
||||
This is done because when giving the context of Chatter on the
|
||||
various mailboxes, we do not have access to the current partner_id. """
|
||||
if kwargs.get('type') == 'email':
|
||||
return super(res_users, self).message_post(cr, uid, thread_id, context=context, **kwargs)
|
||||
employee_ids = self._message_post_get_eid(cr, uid, thread_id, context=context)
|
||||
if not employee_ids:
|
||||
pass # dpo something
|
||||
for employee_id in employee_ids:
|
||||
res = self.pool.get('hr.employee').message_post(cr, uid, employee_id, context=context, **kwargs)
|
||||
return res
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Update user form !-->
|
||||
<record id="view_users_form_mail" model="ir.ui.view">
|
||||
<field name="name">res.users.form.hr</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="mail.view_users_form_mail"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<field name="display_groups_suggestions" position="after">
|
||||
<field name="display_employees_suggestions"/>
|
||||
</field>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -68,3 +68,8 @@
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.openerp .oe_employee_vignette .oe_followers {
|
||||
width: auto;
|
||||
float: none;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
openerp.hr = function(session) {
|
||||
var _t = session.web._t;
|
||||
var QWeb = session.web.qweb;
|
||||
|
||||
var suggestions = session.suggestions;
|
||||
var removed_suggested_employee = session.suggestions.removed_suggested_employee = [];
|
||||
|
||||
suggestions.Employees = session.web.Widget.extend({
|
||||
events: {
|
||||
'click .oe_suggestion_remove.oe_suggestion_employee': 'stop_employee_suggestion',
|
||||
'click .oe_suggestion_remove_item.oe_suggestion_employee': 'remove_employee_suggestion',
|
||||
'click .oe_suggestion_follow': 'follow_employee',
|
||||
},
|
||||
|
||||
init: function () {
|
||||
this._super(this, arguments);
|
||||
this.hr_employee = new session.web.DataSetSearch(this, 'hr.employee');
|
||||
this.res_users = new session.web.DataSetSearch(this, 'res.users');
|
||||
this.employees = [];
|
||||
},
|
||||
|
||||
start: function () {
|
||||
this._super.apply(this, arguments);
|
||||
return this.fetch_suggested_employee();
|
||||
},
|
||||
|
||||
fetch_suggested_employee: function () {
|
||||
var self = this;
|
||||
var employee = self.hr_employee.call('get_suggested_thread', {'removed_suggested_threads': removed_suggested_employee}).then(function (res) {
|
||||
_(res).each(function (result) {
|
||||
result['image']=self.session.url('/web/binary/image', {model: 'hr.employee', field: 'image_small', id: result.id});
|
||||
});
|
||||
self.employees = res;
|
||||
});
|
||||
return $.when(employee).done(this.proxy('display_suggested_employees'));
|
||||
},
|
||||
|
||||
display_suggested_employees: function () {
|
||||
var suggested_employees = this.$('.oe_sidebar_suggestion.oe_suggestion_employee');
|
||||
if (suggested_employees) {
|
||||
suggested_employees.remove();
|
||||
}
|
||||
if (this.employees.length === 0) {
|
||||
return this.$el.empty();
|
||||
}
|
||||
return this.$el.empty().html(QWeb.render('hr.suggestions.employees', {'widget': this}));
|
||||
},
|
||||
|
||||
follow_employee: function (event) {
|
||||
var self = this;
|
||||
var employee_id = parseInt($(event.currentTarget).attr('id'), 10);
|
||||
return this.hr_employee.call('message_subscribe_users', [[employee_id], [this.session.uid], undefined]).then(function(res) {
|
||||
self.fetch_suggested_employee();
|
||||
});
|
||||
},
|
||||
|
||||
remove_employee_suggestion: function (event) {
|
||||
removed_suggested_employee.push($(event.currentTarget).attr('id'));
|
||||
return this.fetch_suggested_employee();
|
||||
},
|
||||
|
||||
stop_employee_suggestion: function (event) {
|
||||
var self = this;
|
||||
return this.res_users.call('stop_showing_employees_suggestions', [this.session.uid]).then(function(res) {
|
||||
self.$(".oe_sidebar_suggestion.oe_suggestion_employee").hide();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
session.mail.WallSidebar.include({
|
||||
start: function () {
|
||||
this._super.apply(this, arguments);
|
||||
var sug_employees = new suggestions.Employees(this);
|
||||
return sug_employees.appendTo(this.$('.oe_suggestions_employees'));
|
||||
},
|
||||
});
|
||||
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
|
||||
<!-- Employees placeholder in sidebar -->
|
||||
<t t-extend="mail.wall.sidebar">
|
||||
<t t-jquery=".oe_mail_wall_sidebar" t-operation="append">
|
||||
<div class="oe_suggestions_employees"></div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- Suggested employees -->
|
||||
<div t-name="hr.suggestions.employees" class="oe_sidebar_suggestion oe_suggestion_employee">
|
||||
<div class="oe_suggest_title">
|
||||
<a class="oe_suggestion_remove oe_suggestion_employee oe_e">X</a>
|
||||
<h2>Suggested Employees</h2>
|
||||
</div>
|
||||
<div class="oe_suggest_items">
|
||||
<t t-foreach="widget.employees" t-as="result">
|
||||
<div class="oe_suggested_item">
|
||||
<div class="oe_suggested_item_image">
|
||||
<a t-attf-href="#model=hr.employee&id=#{result.id}">
|
||||
<img t-attf-src="{result.image}" t-attf-alt="{result.name}"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="oe_suggested_item_content">
|
||||
<a class="oe_suggestion_item_name" t-attf-href="#model=hr.employee&id=#{result.id}"><t t-esc="result.name"/></a>
|
||||
<a class="oe_suggestion_remove_item oe_suggestion_employee oe_e" t-attf-id="{result.id}">X</a>
|
||||
<br/>
|
||||
<button class="oe_suggestion_follow" t-att-id="result.id">Follow</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
@@ -165,9 +165,14 @@
|
||||
</header>
|
||||
<sheet>
|
||||
<label for="employee_id" class="oe_edit_only"/>
|
||||
<h1><field name="employee_id" on_change="onchange_employee_id(employee_id)" class="oe_inline"/>, <field name="date" class="oe_inline"/></h1>
|
||||
<h1><field name="employee_id" class="oe_inline"
|
||||
attrs="{'readonly': [('state', '=', 'done')]}"
|
||||
on_change="onchange_employee_id(employee_id)"/>,
|
||||
<field name="date"
|
||||
attrs="{'readonly': [('state', '=', 'done')]}"/>
|
||||
</h1>
|
||||
<label for="plan_id" class="oe_edit_only"/>
|
||||
<h2><field name="plan_id"/></h2>
|
||||
<h2><field name="plan_id" attrs="{'readonly': [('state', '=', 'done')]}"/></h2>
|
||||
<group>
|
||||
<group colspan="4" attrs="{'invisible':['|', ('state','=','draft'), ('state', '=', 'wait')]}">
|
||||
<field name="rating" attrs="{'readonly':[('state','<>','progress')]}"/>
|
||||
@@ -175,7 +180,7 @@
|
||||
</group>
|
||||
</group>
|
||||
<group string="Appraisal Forms" attrs="{'invisible':[('state','=','draft')]}">
|
||||
<field nolabel="1" name="survey_request_ids">
|
||||
<field nolabel="1" name="survey_request_ids" attrs="{'readonly': [('state', '=', 'done')]}">
|
||||
<form string="Interview Appraisal" version="7.0">
|
||||
<div class="oe_right oe_button_box">
|
||||
<button name="%(survey.action_view_survey_question_message)d" string="Answer Survey" type="action" states="waiting_answer" icon="gtk-execute" context="{'survey_id': survey_id, 'response_id': [response], 'response_no':0, 'active' : response,'request' : True, 'object' : 'hr.evaluation.interview', 'cur_id' : active_id}" attrs="{'readonly':[('survey_id','=',False)]}"/>
|
||||
|
||||
@@ -210,6 +210,11 @@
|
||||
<field name="context">{"default_hr_expense_ok":1}</field>
|
||||
<field name="domain">[('hr_expense_ok','=',True)]</field>
|
||||
<field name="search_view_id" ref="product.product_search_form_view"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new expense category.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_hr_product" name="Expense Categories" parent="hr.menu_hr_configuration" action="hr_expense_product"/>
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
<field name="number_of_days" string="Allocated Days" sum="Remaining Days"/>
|
||||
<field name="manager_id" invisible="1"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
<field name="date_from" invisible="1"/>
|
||||
<!--field name="type"/-->
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<group colspan="4">
|
||||
<field name="month"/>
|
||||
<field name="year"/>
|
||||
<field name="employee_id" colspan="3"/>
|
||||
<field name="employee_id" />
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Print" name="print_report" type="object" class="oe_highlight"/>
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
<menuitem action="action_hr_timesheet_employee"
|
||||
id="menu_hr_timesheet_employee"
|
||||
parent="hr.menu_hr_reporting"
|
||||
parent="hr.menu_hr_reporting_timesheet"
|
||||
sequence="2" icon="STOCK_PRINT"/>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<field name="name">timesheet.report.tree</field>
|
||||
<field name="model">timesheet.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree colors="blue:state == 'draft';black:state in ('confirm','new');gray:state == 'cancel'" string="Timesheet">
|
||||
<tree colors="blue:state == 'draft';black:state in ('confirm','new');gray:state == 'cancel'" string="Timesheet" create="false">
|
||||
<field name="date" invisible="1"/>
|
||||
<field name="name" invisible="1"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
|
||||
@@ -3,7 +3,7 @@ access_hr_timesheet_sheet_sheet_user,hr_timesheet_sheet.sheet.user,model_hr_time
|
||||
access_hr_timesheet_sheet_sheet_system_employee,hr_timesheet_sheet.sheet.system.employee,model_hr_timesheet_sheet_sheet,base.group_user,1,1,1,0
|
||||
access_hr_timesheet_sheet_sheet_day,hr_timesheet_sheet.sheet.day,model_hr_timesheet_sheet_sheet_day,base.group_hr_user,1,1,1,1
|
||||
access_hr_timesheet_sheet_sheet_account,hr_timesheet_sheet.sheet.account,model_hr_timesheet_sheet_sheet_account,base.group_hr_user,1,1,1,1
|
||||
access_hr_timesheet_report,hr.timesheet.report,model_hr_timesheet_report,base.group_hr_manager,1,1,1,1
|
||||
access_hr_timesheet_report,hr.timesheet.report,model_hr_timesheet_report,base.group_hr_manager,1,1,0,0
|
||||
access_hr_analytic_timesheet_system_user,hr.analytic.timesheet.system.user,model_hr_analytic_timesheet,base.group_user,1,0,0,0
|
||||
access_hr_timesheet_sheet_sheet_day,hr.timesheet.sheet.sheet.day.user,model_hr_timesheet_sheet_sheet_day,base.group_user,1,1,1,0
|
||||
access_timesheet_report,timesheet.report,model_timesheet_report,base.group_hr_manager,1,1,1,1
|
||||
access_timesheet_report,timesheet.report,model_timesheet_report,base.group_hr_manager,1,1,0,0
|
||||
|
||||
|
@@ -190,8 +190,13 @@ openerp.hr_timesheet_sheet = function(instance) {
|
||||
$(this).val(self.sum_box(account, day_count, true));
|
||||
} else {
|
||||
account.days[day_count].lines[0].unit_amount += num - self.sum_box(account, day_count);
|
||||
self.display_totals();
|
||||
self.sync();
|
||||
var product = (account.days[day_count].lines[0].product_id instanceof Array) ? account.days[day_count].lines[0].product_id[0] : account.days[day_count].lines[0].product_id
|
||||
var journal = (account.days[day_count].lines[0].journal_id instanceof Array) ? account.days[day_count].lines[0].journal_id[0] : account.days[day_count].lines[0].journal_id
|
||||
new instance.web.Model("hr.analytic.timesheet").call("on_change_unit_amount", [[], product, account.days[day_count].lines[0].unit_amount, false, false, journal]).then(function(res) {
|
||||
account.days[day_count].lines[0]['amount'] = res.value.amount || 0;
|
||||
self.display_totals();
|
||||
self.sync();
|
||||
});
|
||||
if(!isNaN($(this).val())){
|
||||
$(this).val(self.sum_box(account, day_count, true));
|
||||
}
|
||||
@@ -308,10 +313,10 @@ openerp.hr_timesheet_sheet = function(instance) {
|
||||
generate_o2m_value: function() {
|
||||
var self = this;
|
||||
var ops = [];
|
||||
|
||||
|
||||
_.each(self.accounts, function(account) {
|
||||
var auth_keys = _.extend(_.clone(account.account_defaults), {
|
||||
name: true, unit_amount: true, date: true, account_id:true,
|
||||
name: true, amount:true, unit_amount: true, date: true, account_id:true,
|
||||
});
|
||||
_.each(account.days, function(day) {
|
||||
_.each(day.lines, function(line) {
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<para style="terp_default_9">[[ l['vat'] ]]</para>
|
||||
</td>
|
||||
<td>
|
||||
<para style="terp_default_9">[[ l['code'] (l['intra_code']) ]]</para>
|
||||
<para style="terp_default_9">[[ l['code'] ]]([[ l['intra_code'] ]])</para>
|
||||
</td>
|
||||
<td>
|
||||
<para style="terp_default_Right_9">[[ formatLang(l['amount'], currency_obj=company.currency_id) ]]</para>
|
||||
|
||||
@@ -35,24 +35,24 @@
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_account_bank_statement_line_coda_tree" model="ir.ui.view">
|
||||
<record id="view_account_bank_statement_line_coda_tree" model="ir.ui.view">
|
||||
<field name="name">account.bank.statement.line.coda.tree</field>
|
||||
<field name="model">account.bank.statement.line</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" string="Statement lines">
|
||||
<field name="statement_id" readonly="1" invisible="1"/>
|
||||
<field name="sequence" readonly="1" invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="name"/>
|
||||
<field name="ref"/>
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
|
||||
<field name="type" on_change="onchange_type(partner_id, type)"/>
|
||||
<field name="account_id" options='{"no_open":True}' domain="[('journal_id','=',parent.journal_id), ('company_id', '=', parent.company_id)]"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('company_id', '=', parent.company_id), ('type', '<>', 'view')]"/>
|
||||
<field name="amount"/>
|
||||
<field name="note"/>
|
||||
</tree>
|
||||
<tree editable="bottom" string="Statement lines" create="0">
|
||||
<field name="sequence" readonly="1" invisible="1"/>
|
||||
<field name="statement_id" readonly="1" />
|
||||
<field name="date"/>
|
||||
<field name="name"/>
|
||||
<field name="ref"/>
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
|
||||
<field name="type" on_change="onchange_type(partner_id, type)"/>
|
||||
<field name="account_id" options='{"no_open":True}' domain="[('type', '<>', 'view')]"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('type', '<>', 'view')]"/>
|
||||
<field name="amount"/>
|
||||
<field name="note"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -87,10 +87,12 @@ Main Features
|
||||
'static/src/js/mail.js',
|
||||
'static/src/js/mail_followers.js',
|
||||
'static/src/js/many2many_tags_email.js',
|
||||
'static/src/js/suggestions.js',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/mail.xml',
|
||||
'static/src/xml/mail_followers.xml',
|
||||
'static/src/xml/suggestions.xml',
|
||||
],
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -150,11 +150,17 @@ class mail_notification(osv.Model):
|
||||
|
||||
return footer
|
||||
|
||||
def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None):
|
||||
def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None,
|
||||
force_send=False, user_signature=True):
|
||||
""" Send by email the notification depending on the user preferences
|
||||
|
||||
:param list partners_to_notify: optional list of partner ids restricting
|
||||
the notifications to process
|
||||
:param bool force_send: if True, the generated mail.mail is
|
||||
immediately sent after being created, as if the scheduler
|
||||
was executed for this message only.
|
||||
:param bool user_signature: if True, the generated mail.mail body is
|
||||
the body of the related mail.message with the author's signature
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
@@ -189,8 +195,9 @@ class mail_notification(osv.Model):
|
||||
# add signature
|
||||
body_html = msg.body
|
||||
user_id = msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0] and msg.author_id.user_ids[0].id or None
|
||||
signature_company = self.get_signature_footer(cr, uid, user_id, res_model=msg.model, res_id=msg.res_id, context=context)
|
||||
body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
|
||||
if user_signature:
|
||||
signature_company = self.get_signature_footer(cr, uid, user_id, res_model=msg.model, res_id=msg.res_id, context=context)
|
||||
body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
|
||||
|
||||
references = False
|
||||
if msg.parent_id:
|
||||
@@ -203,13 +210,9 @@ class mail_notification(osv.Model):
|
||||
'recipient_ids': [(4, id) for id in notify_partner_ids],
|
||||
'references': references,
|
||||
}
|
||||
if msg.email_from:
|
||||
mail_values['email_from'] = msg.email_from
|
||||
if msg.reply_to:
|
||||
mail_values['reply_to'] = msg.reply_to
|
||||
mail_mail = self.pool.get('mail.mail')
|
||||
email_notif_id = mail_mail.create(cr, uid, mail_values, context=context)
|
||||
try:
|
||||
return mail_mail.send(cr, uid, [email_notif_id], context=context)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
if force_send:
|
||||
mail_mail.send(cr, uid, [email_notif_id], context=context)
|
||||
return True
|
||||
|
||||
@@ -66,11 +66,11 @@
|
||||
|
||||
<!-- Add followers related menu entries in Settings/Email -->
|
||||
<menuitem name="Followers" id="menu_email_followers" parent="base.menu_email"
|
||||
action="action_view_followers" sequence="30" groups="base.group_no_one"/> -->
|
||||
action="action_view_followers" sequence="30" groups="base.group_no_one"/>
|
||||
|
||||
<!-- Add notifications related menu entry in Settings/Email -->
|
||||
<menuitem name="Notifications" id="menu_email_notifications" parent="base.menu_email"
|
||||
action="action_view_notifications" sequence="35" groups="base.group_no_one"/>
|
||||
action="action_view_notifications" sequence="13" groups="base.group_no_one"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -208,3 +208,12 @@ class mail_group(osv.Model):
|
||||
""" Wrapper because message_unsubscribe_users take a user_ids=None
|
||||
that receive the context without the wrapper. """
|
||||
return self.message_unsubscribe_users(cr, uid, ids, context=context)
|
||||
|
||||
def get_suggested_thread(self, cr, uid, removed_suggested_threads=None, context=None):
|
||||
"""Show the suggestion of groups if display_groups_suggestions if the
|
||||
user perference allows it."""
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||
if not user.display_groups_suggestions:
|
||||
return []
|
||||
else:
|
||||
return super(mail_group, self).get_suggested_thread(cr, uid, removed_suggested_threads, context)
|
||||
|
||||
+24
-22
@@ -55,7 +55,6 @@ class mail_mail(osv.Model):
|
||||
'auto_delete': fields.boolean('Auto Delete',
|
||||
help="Permanently delete this email after sending it, to save space"),
|
||||
'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1),
|
||||
'email_from': fields.char('From', help='Message sender, taken from user preferences.'),
|
||||
'email_to': fields.text('To', help='Message recipients (emails)'),
|
||||
'recipient_ids': fields.many2many('res.partner', string='To (Partners)'),
|
||||
'email_cc': fields.char('Cc', help='Carbon copy message recipients'),
|
||||
@@ -67,16 +66,13 @@ class mail_mail(osv.Model):
|
||||
}
|
||||
|
||||
def _get_default_from(self, cr, uid, context=None):
|
||||
this = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
|
||||
if this.alias_domain:
|
||||
return '%s <%s@%s>' % (this.name, this.alias_name, this.alias_domain)
|
||||
elif this.email:
|
||||
return '%s <%s>' % (this.name, this.email)
|
||||
raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias."))
|
||||
""" Kept for compatibility
|
||||
TDE TODO: remove me in 8.0
|
||||
"""
|
||||
return self.pool['mail.message']._get_default_from(cr, uid, context=context)
|
||||
|
||||
_defaults = {
|
||||
'state': 'outgoing',
|
||||
'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx),
|
||||
}
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
@@ -93,19 +89,25 @@ class mail_mail(osv.Model):
|
||||
# if value specified: directly return it
|
||||
if values.get('reply_to'):
|
||||
return values.get('reply_to')
|
||||
format_name = True # whether to use a 'Followers of Pigs <pigs@openerp.com' format
|
||||
email_reply_to = None
|
||||
|
||||
mailgateway = True # tells whether the answer will go through the mailgateway, leading to the formatting of reply_to <Followers of ...>
|
||||
ir_config_parameter = self.pool.get("ir.config_parameter")
|
||||
catchall_domain = ir_config_parameter.get_param(cr, uid, "mail.catchall.domain", context=context)
|
||||
|
||||
# model, res_id, email_from, reply_to: comes from values OR related message
|
||||
message = None
|
||||
# model, res_id, email_from: comes from values OR related message
|
||||
model, res_id, email_from = values.get('model'), values.get('res_id'), values.get('email_from')
|
||||
if values.get('mail_message_id'):
|
||||
message = self.pool.get('mail.message').browse(cr, uid, values.get('mail_message_id'), context=context)
|
||||
model = values.get('model', message and message.model or False)
|
||||
res_id = values.get('res_id', message and message.res_id or False)
|
||||
email_from = values.get('email_from', message and message.email_from or False)
|
||||
email_reply_to = message and message.reply_to or False
|
||||
if message.reply_to:
|
||||
email_reply_to = message.reply_to
|
||||
format_name = False
|
||||
if not model:
|
||||
model = message.model
|
||||
if not res_id:
|
||||
res_id = message.res_id
|
||||
if not email_from:
|
||||
email_from = message.email_from
|
||||
|
||||
# if model and res_id: try to use ``message_get_reply_to`` that returns the document alias
|
||||
if not email_reply_to and model and res_id and hasattr(self.pool[model], 'message_get_reply_to'):
|
||||
@@ -115,16 +117,16 @@ class mail_mail(osv.Model):
|
||||
catchall_alias = ir_config_parameter.get_param(cr, uid, "mail.catchall.alias", context=context)
|
||||
if catchall_domain and catchall_alias:
|
||||
email_reply_to = '%s@%s' % (catchall_alias, catchall_domain)
|
||||
# no alias reply_to -> reply_to will be the email_from, only the email part
|
||||
|
||||
# still no reply_to -> reply_to will be the email_from
|
||||
if not email_reply_to and email_from:
|
||||
emails = tools.email_split(email_from)
|
||||
if emails:
|
||||
email_reply_to = emails[0]
|
||||
if emails[0].split('@')[1] != catchall_domain:
|
||||
mailgateway = False
|
||||
email_reply_to = email_from
|
||||
|
||||
# format 'Document name <email_address>'
|
||||
if email_reply_to and model and res_id and mailgateway:
|
||||
if email_reply_to and model and res_id and format_name:
|
||||
emails = tools.email_split(email_reply_to)
|
||||
if emails:
|
||||
email_reply_to = emails[0]
|
||||
document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0]
|
||||
if document_name:
|
||||
# sanitize document name
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
<field name="res_model">mail.mail</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'search_default_outgoing': 1, 'search_default_type_email': 1}</field>
|
||||
<field name="context">{'search_default_outgoing': 1}</field>
|
||||
<field name="search_view_id" ref="view_mail_search"/>
|
||||
</record>
|
||||
|
||||
|
||||
+27
-15
@@ -196,6 +196,14 @@ class mail_message(osv.Model):
|
||||
def _needaction_domain_get(self, cr, uid, context=None):
|
||||
return [('to_read', '=', True)]
|
||||
|
||||
def _get_default_from(self, cr, uid, context=None):
|
||||
this = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
|
||||
if this.alias_domain:
|
||||
return '%s <%s@%s>' % (this.name, this.alias_name, this.alias_domain)
|
||||
elif this.email:
|
||||
return '%s <%s>' % (this.name, this.email)
|
||||
raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias."))
|
||||
|
||||
def _get_default_author(self, cr, uid, context=None):
|
||||
return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
|
||||
@@ -204,7 +212,7 @@ class mail_message(osv.Model):
|
||||
'date': lambda *a: fields.datetime.now(),
|
||||
'author_id': lambda self, cr, uid, ctx=None: self._get_default_author(cr, uid, ctx),
|
||||
'body': '',
|
||||
'email_from': lambda self, cr, uid, ctx=None: self.pool.get('mail.mail')._get_default_from(cr, uid, ctx),
|
||||
'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx),
|
||||
}
|
||||
|
||||
#------------------------------------------------------
|
||||
@@ -329,8 +337,10 @@ class mail_message(osv.Model):
|
||||
for key, message in message_tree.iteritems():
|
||||
if message.author_id:
|
||||
partner_ids |= set([message.author_id.id])
|
||||
if message.notified_partner_ids:
|
||||
if message.subtype_id and message.notified_partner_ids: # take notified people of message with a subtype
|
||||
partner_ids |= set([partner.id for partner in message.notified_partner_ids])
|
||||
elif not message.subtype_id and message.partner_ids: # take specified people of message without a subtype (log)
|
||||
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])
|
||||
# Read partners as SUPERUSER -> display the names like classic m2o even if no access
|
||||
@@ -350,9 +360,12 @@ class mail_message(osv.Model):
|
||||
else:
|
||||
author = (0, message.email_from)
|
||||
partner_ids = []
|
||||
for partner in message.notified_partner_ids:
|
||||
if partner.id in partner_tree:
|
||||
partner_ids.append(partner_tree[partner.id])
|
||||
if message.subtype_id:
|
||||
partner_ids = [partner_tree[partner.id] for partner in message.notified_partner_ids
|
||||
if partner.id in partner_tree]
|
||||
else:
|
||||
partner_ids = [partner_tree[partner.id] for partner in message.partner_ids
|
||||
if partner.id in partner_tree]
|
||||
attachment_ids = []
|
||||
for attachment in message.attachment_ids:
|
||||
if attachment.id in attachments_tree:
|
||||
@@ -766,7 +779,9 @@ class mail_message(osv.Model):
|
||||
elif not values.get('message_id'):
|
||||
values['message_id'] = tools.generate_tracking_message_id('private')
|
||||
newid = super(mail_message, self).create(cr, uid, values, context)
|
||||
self._notify(cr, uid, newid, context=context)
|
||||
self._notify(cr, uid, newid, context=context,
|
||||
force_send=context.get('mail_notify_force_send', True),
|
||||
user_signature=context.get('mail_notify_user_signature', True))
|
||||
# TDE FIXME: handle default_starred. Why not setting an inv on starred ?
|
||||
# Because starred will call set_message_starred, that looks for notifications.
|
||||
# When creating a new mail_message, it will create a notification to a message
|
||||
@@ -880,20 +895,16 @@ class mail_message(osv.Model):
|
||||
return ''
|
||||
return result
|
||||
|
||||
def _notify(self, cr, uid, newid, context=None):
|
||||
def _notify(self, cr, uid, newid, context=None, force_send=False, user_signature=True):
|
||||
""" Add the related record followers to the destination partner_ids if is not a private message.
|
||||
Call mail_notification.notify to manage the email sending
|
||||
"""
|
||||
notification_obj = self.pool.get('mail.notification')
|
||||
message = self.browse(cr, uid, newid, context=context)
|
||||
|
||||
partners_to_notify = set([])
|
||||
# message has no subtype_id: pure log message -> no partners, no one notified
|
||||
if not message.subtype_id:
|
||||
return True
|
||||
|
||||
# all followers of the mail.message document have to be added as partners and notified
|
||||
if message.model and message.res_id:
|
||||
# all followers of the mail.message document have to be added as partners and notified if a subtype is defined (otherwise: log message)
|
||||
if message.subtype_id and message.model and message.res_id:
|
||||
fol_obj = self.pool.get("mail.followers")
|
||||
# browse as SUPERUSER because rules could restrict the search results
|
||||
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
|
||||
@@ -903,7 +914,7 @@ class mail_message(osv.Model):
|
||||
], context=context)
|
||||
partners_to_notify |= set(fo.partner_id for fo in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context))
|
||||
# remove me from notified partners, unless the message is written on my own wall
|
||||
if message.author_id and message.model == "res.partner" and message.res_id == message.author_id.id:
|
||||
if message.subtype_id and message.author_id and message.model == "res.partner" and message.res_id == message.author_id.id:
|
||||
partners_to_notify |= set([message.author_id])
|
||||
elif message.author_id:
|
||||
partners_to_notify -= set([message.author_id])
|
||||
@@ -914,7 +925,8 @@ class mail_message(osv.Model):
|
||||
|
||||
# notify
|
||||
if partners_to_notify:
|
||||
notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context)
|
||||
notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context,
|
||||
force_send=force_send, user_signature=user_signature)
|
||||
message.refresh()
|
||||
|
||||
# An error appear when a user receive a notification without notifying
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
<field name="subject"/>
|
||||
<field name="author_id"/>
|
||||
<field name="email_from"/>
|
||||
<field name="reply_to"/>
|
||||
<field name="date"/>
|
||||
<field name="type"/>
|
||||
<field name="subtype_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="model"/>
|
||||
@@ -40,6 +40,7 @@
|
||||
<field name="parent_id"/>
|
||||
<field name="partner_ids" widget="many2many_tags"/>
|
||||
<field name="notified_partner_ids" widget="many2many_tags"/>
|
||||
<field name="subtype_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="body"/>
|
||||
|
||||
+92
-28
@@ -240,21 +240,42 @@ class mail_thread(osv.AbstractModel):
|
||||
fol_obj.create(cr, SUPERUSER_ID, {'res_model': self._name, 'res_id': id, 'partner_id': partner_id})
|
||||
|
||||
def _search_followers(self, cr, uid, obj, name, args, context):
|
||||
"""Search function for message_follower_ids
|
||||
|
||||
Do not use with operator 'not in'. Use instead message_is_followers
|
||||
"""
|
||||
fol_obj = self.pool.get('mail.followers')
|
||||
res = []
|
||||
for field, operator, value in args:
|
||||
assert field == name
|
||||
# TOFIX make it work with not in
|
||||
assert operator != "not in", "Do not search message_follower_ids with 'not in'"
|
||||
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('partner_id', operator, value)])
|
||||
res_ids = [fol.res_id for fol in fol_obj.browse(cr, SUPERUSER_ID, fol_ids)]
|
||||
res.append(('id', 'in', res_ids))
|
||||
return res
|
||||
|
||||
def _search_is_follower(self, cr, uid, obj, name, args, context):
|
||||
"""Search function for message_is_follower"""
|
||||
fol_obj = self.pool.get('mail.followers')
|
||||
res = []
|
||||
for field, operator, value in args:
|
||||
assert field == name
|
||||
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
|
||||
if (operator == '=' and value) or (operator == '!=' and not value): # is a follower
|
||||
res_ids = self.search(cr, uid, [('message_follower_ids', 'in', [partner_id])], context=context)
|
||||
else: # is not a follower or unknown domain
|
||||
mail_ids = self.search(cr, uid, [('message_follower_ids', 'in', [partner_id])], context=context)
|
||||
res_ids = self.search(cr, uid, [('id', 'not in', mail_ids)], context=context)
|
||||
res.append(('id', 'in', res_ids))
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'message_is_follower': fields.function(_get_followers,
|
||||
type='boolean', string='Is a Follower', multi='_get_followers,'),
|
||||
'message_is_follower': fields.function(_get_followers, type='boolean',
|
||||
fnct_search=_search_is_follower, string='Is a Follower', multi='_get_followers,'),
|
||||
'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers,
|
||||
fnct_search=_search_followers, type='many2many',
|
||||
obj='res.partner', string='Followers', multi='_get_followers'),
|
||||
fnct_search=_search_followers, type='many2many',
|
||||
obj='res.partner', string='Followers', multi='_get_followers'),
|
||||
'message_ids': fields.one2many('mail.message', 'res_id',
|
||||
domain=lambda self: [('model', '=', self._name)],
|
||||
auto_join=True,
|
||||
@@ -559,7 +580,7 @@ class mail_thread(osv.AbstractModel):
|
||||
ret_dict = {}
|
||||
for model_name in self.pool.obj_list():
|
||||
model = self.pool[model_name]
|
||||
if 'mail.thread' in getattr(model, '_inherit', []):
|
||||
if hasattr(model, "message_process") and hasattr(model, "message_post"):
|
||||
ret_dict[model_name] = model._description
|
||||
return ret_dict
|
||||
|
||||
@@ -794,6 +815,9 @@ class mail_thread(osv.AbstractModel):
|
||||
else:
|
||||
assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
|
||||
model_pool = self.pool.get('mail.thread')
|
||||
if not hasattr(model_pool, 'message_post'):
|
||||
context['thread_model'] = model
|
||||
model_pool = self.pool['mail.thread']
|
||||
new_msg_id = model_pool.message_post(cr, uid, [thread_id], context=context, subtype='mail.mt_comment', **msg)
|
||||
|
||||
if partner_ids:
|
||||
@@ -1128,7 +1152,7 @@ class mail_thread(osv.AbstractModel):
|
||||
model = False
|
||||
if thread_id:
|
||||
model = context.get('thread_model', self._name) if self._name == 'mail.thread' else self._name
|
||||
if model != self._name:
|
||||
if model != self._name and hasattr(self.pool[model], 'message_post'):
|
||||
del context['thread_model']
|
||||
return self.pool[model].message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs)
|
||||
|
||||
@@ -1287,7 +1311,10 @@ class mail_thread(osv.AbstractModel):
|
||||
|
||||
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
|
||||
""" Add partners to the records followers. """
|
||||
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
mail_followers_obj = self.pool.get('mail.followers')
|
||||
subtype_obj = self.pool.get('mail.message.subtype')
|
||||
|
||||
user_pid = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
|
||||
if set(partner_ids) == set([user_pid]):
|
||||
try:
|
||||
self.check_access_rights(cr, uid, 'read')
|
||||
@@ -1296,27 +1323,35 @@ class mail_thread(osv.AbstractModel):
|
||||
else:
|
||||
self.check_access_rights(cr, uid, 'write')
|
||||
|
||||
# subscribe partners
|
||||
self.write(cr, SUPERUSER_ID, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context)
|
||||
for record in self.browse(cr, SUPERUSER_ID, ids, context=context):
|
||||
existing_pids = set([f.id for f in record.message_follower_ids
|
||||
if f.id in partner_ids])
|
||||
new_pids = set(partner_ids) - existing_pids
|
||||
|
||||
# subtype specified: update the subscriptions
|
||||
fol_obj = self.pool.get('mail.followers')
|
||||
if subtype_ids is not None:
|
||||
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)], context=context)
|
||||
fol_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
|
||||
# no subtypes: default ones for new subscription, do not update existing subscriptions
|
||||
else:
|
||||
# search new subscriptions: subtype_ids is False
|
||||
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
|
||||
('res_model', '=', self._name),
|
||||
('res_id', 'in', ids),
|
||||
('partner_id', 'in', partner_ids),
|
||||
('subtype_ids', '=', False)
|
||||
], context=context)
|
||||
if fol_ids:
|
||||
subtype_obj = self.pool.get('mail.message.subtype')
|
||||
subtype_ids = subtype_obj.search(cr, uid, [('default', '=', True), '|', ('res_model', '=', self._name), ('res_model', '=', False)], context=context)
|
||||
fol_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
|
||||
# subtype_ids specified: update already subscribed partners
|
||||
if subtype_ids and existing_pids:
|
||||
fol_ids = mail_followers_obj.search(cr, SUPERUSER_ID, [
|
||||
('res_model', '=', self._name),
|
||||
('res_id', '=', record.id),
|
||||
('partner_id', 'in', list(existing_pids)),
|
||||
], context=context)
|
||||
mail_followers_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
|
||||
# subtype_ids not specified: do not update already subscribed partner, fetch default subtypes for new partners
|
||||
else:
|
||||
subtype_ids = subtype_obj.search(cr, uid, [
|
||||
('default', '=', True),
|
||||
'|',
|
||||
('res_model', '=', self._name),
|
||||
('res_model', '=', False)
|
||||
], context=context)
|
||||
# subscribe new followers
|
||||
for new_pid in new_pids:
|
||||
mail_followers_obj.create(cr, SUPERUSER_ID, {
|
||||
'res_model': self._name,
|
||||
'res_id': record.id,
|
||||
'partner_id': new_pid,
|
||||
'subtype_ids': [(6, 0, subtype_ids)],
|
||||
}, context=context)
|
||||
|
||||
return True
|
||||
|
||||
@@ -1454,4 +1489,33 @@ class mail_thread(osv.AbstractModel):
|
||||
''', (ids, self._name, partner_id))
|
||||
return True
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
#------------------------------------------------------
|
||||
# Thread suggestion
|
||||
#------------------------------------------------------
|
||||
|
||||
def get_suggested_thread(self, cr, uid, removed_suggested_threads=None, context=None):
|
||||
"""Return a list of suggested threads, sorted by the numbers of followers"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
# TDE HACK: originally by MAT from portal/mail_mail.py but not working until the inheritance graph bug is not solved in trunk
|
||||
# TDE FIXME: relocate in portal when it won't be necessary to reload the hr.employee model in an additional bridge module
|
||||
if self.pool['res.groups']._all_columns.get('is_portal'):
|
||||
user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
|
||||
if any(group.is_portal for group in user.groups_id):
|
||||
return []
|
||||
|
||||
threads = []
|
||||
if removed_suggested_threads is None:
|
||||
removed_suggested_threads = []
|
||||
|
||||
thread_ids = self.search(cr, uid, [('id', 'not in', removed_suggested_threads), ('message_is_follower', '=', False)], context=context)
|
||||
for thread in self.browse(cr, uid, thread_ids, context=context):
|
||||
data = {
|
||||
'id': thread.id,
|
||||
'popularity': len(thread.message_follower_ids),
|
||||
'name': thread.name,
|
||||
'image_small': thread.image_small
|
||||
}
|
||||
threads.append(data)
|
||||
return sorted(threads, key=lambda x: (x['popularity'], x['id']), reverse=True)[:3]
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
],
|
||||
'view_mailbox': True,
|
||||
'view_inbox': True,
|
||||
'read_action': 'read'
|
||||
'read_action': 'read',
|
||||
'show_compose_message': False
|
||||
}""/>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.tools.translate import _
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
@@ -52,21 +53,4 @@ class res_partner_mail(osv.Model):
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, partner, partner=partner, reason=_('Partner Profile'))
|
||||
return recipients
|
||||
|
||||
def message_post(self, cr, uid, thread_id, **kwargs):
|
||||
""" Override related to res.partner. In case of email message, set it as
|
||||
private:
|
||||
- add the target partner in the message partner_ids
|
||||
- set thread_id as None, because this will trigger the 'private'
|
||||
aspect of the message (model=False, res_id=False)
|
||||
"""
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id[0]
|
||||
if kwargs.get('type') == 'email':
|
||||
partner_ids = kwargs.get('partner_ids', [])
|
||||
if thread_id not in [command[1] for command in partner_ids]:
|
||||
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, **kwargs)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
+20
-29
@@ -28,6 +28,7 @@ class res_users(osv.Model):
|
||||
- add a preference about sending emails about notifications
|
||||
- make a new user follow itself
|
||||
- add a welcome message
|
||||
- add suggestion preference
|
||||
"""
|
||||
_name = 'res.users'
|
||||
_inherit = ['res.users']
|
||||
@@ -37,10 +38,12 @@ class res_users(osv.Model):
|
||||
'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True,
|
||||
help="Email address internally associated with this user. Incoming "\
|
||||
"emails will appear in the user's notifications."),
|
||||
'display_groups_suggestions': fields.boolean("Display Groups Suggestions"),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'alias_domain': False, # always hide alias during creation
|
||||
'display_groups_suggestions': True,
|
||||
}
|
||||
|
||||
def __init__(self, pool, cr):
|
||||
@@ -51,10 +54,10 @@ class res_users(osv.Model):
|
||||
init_res = super(res_users, self).__init__(pool, cr)
|
||||
# duplicate list to avoid modifying the original reference
|
||||
self.SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS)
|
||||
self.SELF_WRITEABLE_FIELDS.append('notification_email_send')
|
||||
self.SELF_WRITEABLE_FIELDS.extend(['notification_email_send', 'display_groups_suggestions'])
|
||||
# duplicate list to avoid modifying the original reference
|
||||
self.SELF_READABLE_FIELDS = list(self.SELF_READABLE_FIELDS)
|
||||
self.SELF_READABLE_FIELDS.extend(['notification_email_send', 'alias_domain', 'alias_name'])
|
||||
self.SELF_READABLE_FIELDS.extend(['notification_email_send', 'alias_domain', 'alias_name', 'display_groups_suggestions'])
|
||||
return init_res
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
@@ -109,7 +112,7 @@ class res_users(osv.Model):
|
||||
def _message_post_get_pid(self, cr, uid, thread_id, context=None):
|
||||
assert thread_id, "res.users does not support posting global messages"
|
||||
if context and 'thread_model' in context:
|
||||
context['thread_model'] = 'res.partner'
|
||||
context['thread_model'] = 'res.users'
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id[0]
|
||||
return self.browse(cr, SUPERUSER_ID, thread_id).partner_id.id
|
||||
@@ -118,44 +121,32 @@ class res_users(osv.Model):
|
||||
""" Redirect the posting of message on res.users to the related partner.
|
||||
This is done because when giving the context of Chatter on the
|
||||
various mailboxes, we do not have access to the current partner_id. """
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id[0]
|
||||
partner_ids = kwargs.get('partner_ids', [])
|
||||
partner_id = self._message_post_get_pid(cr, uid, thread_id, context=context)
|
||||
return self.pool.get('res.partner').message_post(cr, uid, partner_id, context=context, **kwargs)
|
||||
if partner_id not in [command[1] for command in partner_ids]:
|
||||
partner_ids.append(partner_id)
|
||||
kwargs['partner_ids'] = partner_ids
|
||||
return self.pool.get('mail.thread').message_post(cr, uid, False, **kwargs)
|
||||
|
||||
def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
|
||||
for id in ids:
|
||||
partner_id = self.browse(cr, SUPERUSER_ID, id).partner_id.id
|
||||
self.pool.get('res.partner').message_update(cr, uid, [partner_id], msg_dict, update_vals=update_vals, context=context)
|
||||
return True
|
||||
|
||||
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
|
||||
for id in ids:
|
||||
partner_id = self.browse(cr, SUPERUSER_ID, id).partner_id.id
|
||||
self.pool.get('res.partner').message_subscribe(cr, uid, [partner_id], partner_ids, subtype_ids=subtype_ids, context=context)
|
||||
return True
|
||||
|
||||
def message_get_partner_info_from_emails(self, cr, uid, emails, link_mail=False, context=None):
|
||||
return self.pool.get('res.partner').message_get_partner_info_from_emails(cr, uid, emails, link_mail=link_mail, context=context)
|
||||
return self.pool.get('mail.thread').message_get_partner_info_from_emails(cr, uid, emails, link_mail=link_mail, context=context)
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
partner_ids = []
|
||||
for id in ids:
|
||||
partner_ids.append(self.browse(cr, SUPERUSER_ID, id).partner_id.id)
|
||||
return self.pool.get('res.partner').message_get_suggested_recipients(cr, uid, partner_ids, context=context)
|
||||
return dict.fromkeys(ids, list())
|
||||
|
||||
#------------------------------------------------------
|
||||
# Compatibility methods: do not use
|
||||
# TDE TODO: remove me in 8.0
|
||||
#------------------------------------------------------
|
||||
|
||||
def message_post_user_api(self, cr, uid, thread_id, context=None, **kwargs):
|
||||
""" Redirect the posting of message on res.users to the related partner.
|
||||
This is done because when giving the context of Chatter on the
|
||||
various mailboxes, we do not have access to the current partner_id. """
|
||||
partner_id = self._message_post_get_pid(cr, uid, thread_id, context=context)
|
||||
return self.pool.get('res.partner').message_post_user_api(cr, uid, partner_id, context=context, **kwargs)
|
||||
|
||||
def message_create_partners_from_emails(self, cr, uid, emails, context=None):
|
||||
return self.pool.get('res.partner').message_create_partners_from_emails(cr, uid, emails, context=context)
|
||||
def stop_showing_groups_suggestions(self, cr, uid, user_id, context=None):
|
||||
"""Update display_groups_suggestions value to False"""
|
||||
if context is None:
|
||||
context = {}
|
||||
self.write(cr, uid, user_id, {"display_groups_suggestions": False}, context)
|
||||
|
||||
|
||||
class res_users_mail_group(osv.Model):
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
<field name="alias_domain" invisible="1"/>
|
||||
<field name="alias_id" readonly="1" required="0" attrs="{'invisible': [('alias_domain', '=', False)]}"/>
|
||||
</field>
|
||||
<group string="Email preferences" position="after">
|
||||
<group name="misc" string="Miscellaneous"
|
||||
groups="base.group_no_one">
|
||||
<field name="display_groups_suggestions"/>
|
||||
</group>
|
||||
</group>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0px;
|
||||
margin: 0px
|
||||
margin: 0px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
@@ -665,8 +665,72 @@
|
||||
.openerp .oe_mail_wall .oe_mail{
|
||||
margin: 16px;
|
||||
width: 600px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_view_nocontent > p {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
|
||||
/* ------------- WALL SIDEBAR ------------- */
|
||||
|
||||
.openerp .oe_mail_wall .oe_mail_wall_aside {
|
||||
margin: 16px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 260px;
|
||||
}
|
||||
.openerp .oe_mail_wall_aside .oe_sidebar_suggestion {
|
||||
background-color: #EDEDF6;
|
||||
border-radius: 2px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_title h2 {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
padding: 0px;
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item {
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
margin-left: 10px;
|
||||
min-height: 67px; /* image_small 66x66px */
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_image {
|
||||
float: left;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_image img {
|
||||
border-radius: 2px;
|
||||
border: solid 1px rgba(0,0,0,0.03);
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_content button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_content a.oe_suggestion_item_name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
width: 90%;
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_title a.oe_suggestion_remove {
|
||||
line-height: 15px;
|
||||
margin-top: -2px;
|
||||
float: right;
|
||||
visibility: hidden;
|
||||
margin-right: 7px;
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_items .oe_suggested_item_content a.oe_suggestion_remove_item {
|
||||
line-height: 15px;
|
||||
margin-top: -2px;
|
||||
float: right;
|
||||
visibility: hidden;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_title:hover a.oe_suggestion_remove,
|
||||
.openerp .oe_sidebar_suggestion .oe_suggest_items:hover a.oe_suggestion_remove_item {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
-moz-border-radius: 3px;
|
||||
border-collapse: separate;
|
||||
-webkit-border-radius: 3px;
|
||||
-o-border-radius: 3px;
|
||||
-ms-border-radius: 3px;
|
||||
|
||||
@@ -4,8 +4,8 @@ openerp.mail = function (session) {
|
||||
|
||||
var mail = session.mail = {};
|
||||
|
||||
openerp_mail_followers(session, mail); // import mail_followers.js
|
||||
openerp_FieldMany2ManyTagsEmail(session); // import manyy2many_tags_email.js
|
||||
openerp_mail_followers(session, mail); // import mail_followers.js
|
||||
openerp_FieldMany2ManyTagsEmail(session); // import manyy2many_tags_email.js
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------
|
||||
@@ -237,8 +237,15 @@ openerp.mail = function (session) {
|
||||
|
||||
this.format_data();
|
||||
|
||||
// update record_name: Partner profile
|
||||
if (this.model == 'res.partner') {
|
||||
this.record_name = 'Partner Profile of ' + this.record_name;
|
||||
}
|
||||
else if (this.model == 'hr.employee') {
|
||||
this.record_name = 'News from ' + this.record_name;
|
||||
}
|
||||
// record options and data
|
||||
this.show_record_name = this.options.show_record_name && this.record_name && !this.thread_level && this.model != 'res.partner';
|
||||
this.show_record_name = this.options.show_record_name && this.record_name && !this.thread_level;
|
||||
this.options.show_read = false;
|
||||
this.options.show_unread = false;
|
||||
if (this.options.show_read_unread_button) {
|
||||
@@ -257,8 +264,10 @@ openerp.mail = function (session) {
|
||||
|
||||
/* Convert date, timerelative and avatar in displayable data. */
|
||||
format_data: function () {
|
||||
|
||||
//formating and add some fields for render
|
||||
this.date = this.date ? session.web.str_to_datetime(this.date) : false;
|
||||
this.display_date = this.date.toString('ddd MMM dd yyyy HH:mm');
|
||||
if (this.date && new Date().getTime()-this.date.getTime() < 7*24*60*60*1000) {
|
||||
this.timerelative = $.timeago(this.date);
|
||||
}
|
||||
@@ -745,7 +754,7 @@ openerp.mail = function (session) {
|
||||
}
|
||||
// create object and attach to the thread object
|
||||
thread.message_fetch([["id", "=", message_id]], false, [message_id], function (arg, data) {
|
||||
var message = thread.create_message_object( data[0] );
|
||||
var message = thread.create_message_object( data.slice(-1)[0] );
|
||||
// insert the message on dom
|
||||
thread.insert_message( message, root ? undefined : self.$el, root );
|
||||
});
|
||||
@@ -978,8 +987,8 @@ openerp.mail = function (session) {
|
||||
expender: function () {
|
||||
this.$('.oe_msg_body:first').expander({
|
||||
slicePoint: this.options.truncate_limit,
|
||||
expandText: 'read more',
|
||||
userCollapseText: 'read less',
|
||||
expandText: _t('read more'),
|
||||
userCollapseText: _t('read less'),
|
||||
detailClass: 'oe_msg_tail',
|
||||
moreClass: 'oe_mail_expand',
|
||||
lessClass: 'oe_mail_reduce',
|
||||
@@ -1840,6 +1849,19 @@ openerp.mail = function (session) {
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------
|
||||
* Aside Widget
|
||||
* ------------------------------------------------------------
|
||||
*
|
||||
* This widget handles the display of a sidebar on the Wall. Its main use
|
||||
* is to display group and employees suggestion (if hr is installed).
|
||||
*/
|
||||
mail.WallSidebar = session.web.Widget.extend({
|
||||
template: 'mail.wall.sidebar',
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------
|
||||
* Wall Widget
|
||||
@@ -1901,6 +1923,9 @@ openerp.mail = function (session) {
|
||||
if (! this.searchview.has_defaults) {
|
||||
this.message_render();
|
||||
}
|
||||
// render sidebar
|
||||
var wall_sidebar = new mail.WallSidebar(this);
|
||||
wall_sidebar.appendTo(this.$el.find('.oe_mail_wall_aside'));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2018,4 +2043,16 @@ openerp.mail = function (session) {
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------
|
||||
* Sub-widgets loading
|
||||
* ------------------------------------------------------------
|
||||
*
|
||||
* Load here widgets that could depend on widgets defined in mail.js
|
||||
*/
|
||||
|
||||
openerp.mail.suggestions(session, mail); // import suggestion.js (suggestion widget)
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
openerp.mail.suggestions = function(session, mail) {
|
||||
var _t = session.web._t;
|
||||
var QWeb = session.web.qweb;
|
||||
|
||||
var suggestions = session.suggestions = {};
|
||||
var removed_suggested_group = session.suggestions.removed_suggested_group = [];
|
||||
|
||||
suggestions.Groups = session.web.Widget.extend({
|
||||
events: {
|
||||
'click .oe_suggestion_remove.oe_suggestion_group': 'stop_group_suggestion',
|
||||
'click .oe_suggestion_remove_item.oe_suggestion_group': 'remove_group_suggestion',
|
||||
'click .oe_suggestion_join': 'join_group',
|
||||
},
|
||||
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.mail_group = new session.web.DataSetSearch(this, 'mail.group');
|
||||
this.res_users = new session.web.DataSetSearch(this, 'res.users');
|
||||
this.groups = [];
|
||||
},
|
||||
|
||||
start: function () {
|
||||
this._super.apply(this, arguments);
|
||||
return this.fetch_suggested_groups();
|
||||
},
|
||||
|
||||
fetch_suggested_groups: function () {
|
||||
var self = this;
|
||||
var group = self.mail_group.call('get_suggested_thread', {'removed_suggested_threads': removed_suggested_group}).then(function (res) {
|
||||
_(res).each(function (result) {
|
||||
result['image']=self.session.url('/web/binary/image', {model: 'mail.group', field: 'image_small', id: result.id});
|
||||
});
|
||||
self.groups = res;
|
||||
});
|
||||
return $.when(group).then(this.proxy('display_suggested_groups'));
|
||||
},
|
||||
|
||||
display_suggested_groups: function () {
|
||||
var suggested_groups = this.$('.oe_sidebar_suggestion.oe_suggestion_group');
|
||||
if (suggested_groups) {
|
||||
suggested_groups.empty();
|
||||
}
|
||||
if (this.groups.length === 0) {
|
||||
return this.$el.empty();
|
||||
}
|
||||
return this.$el.empty().html(QWeb.render('mail.suggestions.groups', {'widget': this}));
|
||||
},
|
||||
|
||||
join_group: function (event) {
|
||||
var self = this;
|
||||
return this.mail_group.call('message_subscribe_users', [[$(event.currentTarget).attr('id')],[this.session.uid]]).then(function(res) {
|
||||
self.fetch_suggested_groups();
|
||||
});
|
||||
},
|
||||
|
||||
remove_group_suggestion: function (event) {
|
||||
removed_suggested_group.push($(event.currentTarget).attr('id'));
|
||||
return this.fetch_suggested_groups();
|
||||
},
|
||||
|
||||
stop_group_suggestion: function (event) {
|
||||
var self = this;
|
||||
return this.res_users.call('stop_showing_groups_suggestions', [this.session.uid]).then(function(res) {
|
||||
self.$(".oe_sidebar_suggestion.oe_suggestion_group").hide();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
session.mail.WallSidebar.include({
|
||||
start: function () {
|
||||
this._super.apply(this, arguments);
|
||||
var sug_groups = new suggestions.Groups(this);
|
||||
return sug_groups.appendTo(this.$('.oe_suggestions_groups'));
|
||||
},
|
||||
});
|
||||
|
||||
};
|
||||
@@ -121,9 +121,11 @@
|
||||
To:
|
||||
<t t-if="!widget.is_private">
|
||||
<span class="oe_all_follower">
|
||||
Followers of
|
||||
<t t-if="widget.parent_thread.parent_message.record_name" t-raw="'"' + widget.parent_thread.parent_message.record_name + '"'"/>
|
||||
<t t-if="!widget.parent_thread.parent_message.record_name">this document</t>
|
||||
<t t-if="widget.parent_thread.parent_message.record_name" t-raw="'"' + widget.parent_thread.parent_message.record_name + '"'">
|
||||
Followers of <t t-raw="'"' + widget.parent_thread.parent_message.record_name + '"'"/>
|
||||
</t>
|
||||
<t t-if="!widget.parent_thread.parent_message.record_name and widget.options.view_inbox">My Followers</t>
|
||||
<t t-if="!widget.parent_thread.parent_message.record_name and !widget.options.view_inbox">Followers of this document</t>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="!widget.is_private and (widget.partner_ids.length or (widget.author_id and widget.author_id[0]))"> and </t>
|
||||
@@ -197,8 +199,16 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="oe_mail-placeholder"></div>
|
||||
<aside class="oe_mail_wall_aside"></aside>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Empty template that holds the sidebar of the Wall
|
||||
-->
|
||||
<t t-name="mail.wall.sidebar">
|
||||
<div class="oe_mail_wall_sidebar"></div>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
display message on the wall when there are no message
|
||||
-->
|
||||
@@ -271,16 +281,16 @@
|
||||
<span class='oe_subtle'>•</span>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="widget.type == 'comment' and ! widget.subtype">
|
||||
<t t-if="widget.type == 'comment' and ! widget.subtype and widget.partner_ids.length == 0">
|
||||
logged a note
|
||||
</t>
|
||||
<t t-if="(widget.type == 'comment' or widget.type == 'email') and widget.subtype">
|
||||
<t t-if="(widget.type == 'comment' or widget.type == 'email') and (widget.subtype or widget.partner_ids.length > 0)">
|
||||
to
|
||||
<t t-if="widget.partner_ids.length == 0">
|
||||
nobody
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and widget.subtype)"
|
||||
<t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))"
|
||||
t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
|
||||
<span t-attf-class="oe_partner_follower">
|
||||
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-raw="partner[1]"/></a>
|
||||
@@ -295,7 +305,10 @@
|
||||
notified
|
||||
</t>
|
||||
<span class='oe_subtle'>•</span>
|
||||
<span t-att-title="widget.date"><t t-if="widget.timerelative" t-raw="widget.timerelative"/><t t-if="!widget.timerelative" t-raw="widget.date"/></span>
|
||||
<span t-att-title="widget.date">
|
||||
<t t-if="widget.timerelative" t-raw="widget.timerelative"/>
|
||||
<t t-if="!widget.timerelative" t-raw="widget.display_date"/>
|
||||
</span>
|
||||
<span t-if="!widget.options.readonly" class='oe_subtle'>•</span>
|
||||
<t t-if="!widget.options.readonly" t-call="mail.thread.message.vote"/>
|
||||
</div>
|
||||
@@ -339,7 +352,4 @@
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<!-- mail.thread.message.star
|
||||
Template used to display stared/unstared message in a mail.message
|
||||
-->
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
|
||||
<!-- Groups placeholder in sidebar -->
|
||||
<t t-extend="mail.wall.sidebar">
|
||||
<t t-jquery=".oe_mail_wall_sidebar" t-operation="append">
|
||||
<div class="oe_suggestions_groups"></div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
<!-- Suggested groups -->
|
||||
<div t-name="mail.suggestions.groups" class="oe_sidebar_suggestion oe_suggestion_group">
|
||||
<div class="oe_suggest_title">
|
||||
<a class="oe_suggestion_remove oe_suggestion_group oe_e">X</a>
|
||||
<h2>Suggested Groups</h2>
|
||||
</div>
|
||||
<div class="oe_suggest_items">
|
||||
<t t-foreach="widget.groups" t-as="result">
|
||||
<div class="oe_suggested_item">
|
||||
<div class="oe_suggested_item_image">
|
||||
<a t-attf-href="#model=mail.group&id=#{result.id}">
|
||||
<img t-attf-src="{result.image}" t-attf-alt="{result.name}"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="oe_suggested_item_content">
|
||||
<a class="oe_suggestion_item_name" t-attf-href="#model=mail.group&id=#{result.id}"><t t-esc="result.name"/></a>
|
||||
<a class="oe_suggestion_remove_item oe_suggestion_group oe_e" t-attf-id="{result.id}">X</a>
|
||||
<br/>
|
||||
<button t-att-id="result.id" class="oe_suggestion_join">Join Group</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
@@ -69,6 +69,7 @@ class TestMailBase(common.TransactionCase):
|
||||
self.group_employee_id = group_employee_ref and group_employee_ref[1] or False
|
||||
|
||||
# Test users to use through the various tests
|
||||
self.res_users.write(cr, uid, uid, {'name': 'Administrator'})
|
||||
self.user_raoul_id = self.res_users.create(cr, uid,
|
||||
{'name': 'Raoul Grosbedon', 'signature': 'SignRaoul', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]})
|
||||
self.user_bert_id = self.res_users.create(cr, uid,
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
from openerp.addons.mail.mail_mail import mail_mail
|
||||
from openerp.addons.mail.mail_thread import mail_thread
|
||||
from openerp.addons.mail.tests.test_mail_base import TestMailBase
|
||||
from openerp.tools import mute_logger
|
||||
from openerp.tools import mute_logger, email_split
|
||||
from openerp.tools.mail import html_sanitize
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@ class test_mail(TestMailBase):
|
||||
'message_post: notification email sent to more than one email address instead of a precise partner')
|
||||
self.assertIn(sent_email['email_to'][0], test_emailto,
|
||||
'message_post: notification email email_to incorrect')
|
||||
self.assertEqual(sent_email['reply_to'], 'r@r', # was '"Followers of Pigs" <r@r>', but makes no sense
|
||||
self.assertEqual(email_split(sent_email['reply_to']), ['r@r'], # was '"Followers of Pigs" <r@r>', but makes no sense
|
||||
'message_post: notification email reply_to incorrect: should have raoul email')
|
||||
self.assertEqual(_mail_subject, sent_email['subject'],
|
||||
'message_post: notification email subject incorrect')
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
##############################################################################
|
||||
|
||||
from openerp.addons.mail.tests.test_mail_base import TestMailBase
|
||||
from openerp.tools import mute_logger
|
||||
from openerp.tools import mute_logger, email_split
|
||||
|
||||
MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com>
|
||||
To: {to}
|
||||
@@ -124,6 +124,102 @@ class TestMailgateway(TestMailBase):
|
||||
self.assertEqual(partner_info['partner_id'], p_b_id,
|
||||
'mail_thread: message_find_partner_from_emails wrong partner found')
|
||||
|
||||
def test_05_mail_message_mail_mail(self):
|
||||
""" Tests designed for testing email values based on mail.message, aliases, ... """
|
||||
cr, uid, user_raoul_id = self.cr, self.uid, self.user_raoul_id
|
||||
|
||||
# Data: update + generic variables
|
||||
reply_to1 = '_reply_to1@example.com'
|
||||
reply_to2 = '_reply_to2@example.com'
|
||||
email_from1 = 'from@example.com'
|
||||
alias_domain = 'schlouby.fr'
|
||||
raoul_from = 'Raoul Grosbedon <raoul@raoul.fr>'
|
||||
raoul_from_alias = 'Raoul Grosbedon <raoul@schlouby.fr>'
|
||||
raoul_reply = '"Followers of Pigs" <raoul@raoul.fr>'
|
||||
raoul_reply_alias = '"Followers of Pigs" <group+pigs@schlouby.fr>'
|
||||
# Data: remove alias_domain to see emails with alias
|
||||
param_ids = self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.domain')])
|
||||
self.registry('ir.config_parameter').unlink(cr, uid, param_ids)
|
||||
|
||||
# Do: free message; specified values > default values
|
||||
msg_id = self.mail_message.create(cr, user_raoul_id, {'reply_to': reply_to1, 'email_from': email_from1})
|
||||
msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
|
||||
# Test: message content
|
||||
self.assertIn('reply_to', msg.message_id,
|
||||
'mail_message: message_id should be specific to a mail_message with a given reply_to')
|
||||
self.assertEqual(msg.reply_to, reply_to1,
|
||||
'mail_message: incorrect reply_to: should come from values')
|
||||
self.assertEqual(msg.email_from, email_from1,
|
||||
'mail_message: incorrect email_from: should come from values')
|
||||
# Do: create a mail_mail with the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, reply_to1,
|
||||
'mail_mail: incorrect reply_to: should come from mail.message')
|
||||
self.assertEqual(mail.email_from, email_from1,
|
||||
'mail_mail: incorrect email_from: should come from mail.message')
|
||||
# Do: create a mail_mail with the previous mail_message + specified reply_to
|
||||
mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel', 'reply_to': reply_to2})
|
||||
mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, reply_to2,
|
||||
'mail_mail: incorrect reply_to: should come from values')
|
||||
self.assertEqual(mail.email_from, email_from1,
|
||||
'mail_mail: incorrect email_from: should come from mail.message')
|
||||
|
||||
# Do: mail_message attached to a document
|
||||
msg_id = self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_pigs_id})
|
||||
msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
|
||||
# Test: message content
|
||||
self.assertIn('mail.group', msg.message_id,
|
||||
'mail_message: message_id should contain model')
|
||||
self.assertIn('%s' % self.group_pigs_id, msg.message_id,
|
||||
'mail_message: message_id should contain res_id')
|
||||
self.assertFalse(msg.reply_to,
|
||||
'mail_message: incorrect reply_to: should not be generated if not specified')
|
||||
self.assertEqual(msg.email_from, raoul_from,
|
||||
'mail_message: incorrect email_from: should be Raoul')
|
||||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, raoul_reply,
|
||||
'mail_mail: incorrect reply_to: should be Raoul')
|
||||
|
||||
# Data: set catchall domain
|
||||
self.registry('ir.config_parameter').set_param(cr, uid, 'mail.catchall.domain', alias_domain)
|
||||
|
||||
# Update message
|
||||
self.mail_message.write(cr, user_raoul_id, [msg_id], {'email_from': False, 'reply_to': False})
|
||||
msg.refresh()
|
||||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, raoul_reply_alias,
|
||||
'mail_mail: incorrect reply_to: should be Pigs alias')
|
||||
|
||||
# Update message: test alias on email_from
|
||||
msg_id = self.mail_message.create(cr, user_raoul_id, {})
|
||||
msg = self.mail_message.browse(cr, user_raoul_id, msg_id)
|
||||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, raoul_from_alias,
|
||||
'mail_mail: incorrect reply_to: should be message email_from using Raoul alias')
|
||||
|
||||
# Update message
|
||||
self.mail_message.write(cr, user_raoul_id, [msg_id], {'res_id': False, 'email_from': 'someone@schlouby.fr', 'reply_to': False})
|
||||
msg.refresh()
|
||||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, user_raoul_id, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, msg.email_from,
|
||||
'mail_mail: incorrect reply_to: should be message email_from')
|
||||
|
||||
def test_05_mail_message_mail_mail(self):
|
||||
""" Tests designed for testing email values based on mail.message, aliases, ... """
|
||||
cr, uid = self.cr, self.uid
|
||||
@@ -178,7 +274,7 @@ class TestMailgateway(TestMailBase):
|
||||
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, uid, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, msg.email_from,
|
||||
self.assertEqual(email_split(mail.reply_to), email_split(msg.email_from),
|
||||
'mail_mail: reply_to should be equal to mail_message.email_from when having no document or default alias')
|
||||
|
||||
# Data: set catchall domain
|
||||
|
||||
@@ -126,7 +126,6 @@ class mail_compose_message(osv.TransientModel):
|
||||
|
||||
_defaults = {
|
||||
'composition_mode': 'comment',
|
||||
'email_from': lambda self, cr, uid, ctx={}: self.pool.get('mail.mail')._get_default_from(cr, uid, context=ctx),
|
||||
'body': lambda self, cr, uid, ctx={}: '',
|
||||
'subject': lambda self, cr, uid, ctx={}: False,
|
||||
'partner_ids': lambda self, cr, uid, ctx={}: [],
|
||||
@@ -158,7 +157,7 @@ class mail_compose_message(osv.TransientModel):
|
||||
|
||||
return super(mail_compose_message, self).check_access_rule(cr, uid, ids, operation, context=context)
|
||||
|
||||
def _notify(self, cr, uid, newid, context=None):
|
||||
def _notify(self, cr, uid, newid, context=None, force_send=False, user_signature=True):
|
||||
""" Override specific notify method of mail.message, because we do
|
||||
not want that feature in the wizard. """
|
||||
return
|
||||
@@ -235,6 +234,9 @@ class mail_compose_message(osv.TransientModel):
|
||||
for wizard in self.browse(cr, uid, ids, context=context):
|
||||
mass_mail_mode = wizard.composition_mode == 'mass_mail'
|
||||
active_model_pool = self.pool[wizard.model if wizard.model else 'mail.thread']
|
||||
if not hasattr(active_model_pool, 'message_post'):
|
||||
context['thread_model'] = wizard.model
|
||||
active_model_pool = self.pool['mail.thread']
|
||||
|
||||
# wizard works in batch mode: [res_id] or active_ids
|
||||
res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id]
|
||||
@@ -257,13 +259,15 @@ class mail_compose_message(osv.TransientModel):
|
||||
new_attach_id = ir_attachment_obj.copy(cr, uid, attach_id, {'res_model': self._name, 'res_id': wizard.id}, context=context)
|
||||
attachment_ids.append(new_attach_id)
|
||||
post_values['attachment_ids'] = attachment_ids
|
||||
post_values.update(email_dict)
|
||||
# email_from: mass mailing only can specify another email_from
|
||||
if email_dict.get('email_from'):
|
||||
post_values['email_from'] = email_dict.pop('email_from')
|
||||
# replies redirection: mass mailing only
|
||||
if not wizard.same_thread:
|
||||
post_values['reply_to'] = email_dict.pop('reply_to')
|
||||
else:
|
||||
email_dict.pop('reply_to')
|
||||
post_values.update(email_dict)
|
||||
# clean the context (hint: mass mailing sets some default values that
|
||||
# could be wrongly interpreted by mail_mail)
|
||||
context.pop('default_email_to', None)
|
||||
@@ -280,11 +284,10 @@ class mail_compose_message(osv.TransientModel):
|
||||
elif mass_mail_mode: # mass mail: is a log pushed to recipients unless specified, author not added
|
||||
if not wizard.notify:
|
||||
subtype = False
|
||||
context = dict(context, mail_create_nosubscribe=True) # add context key to avoid subscribing the author
|
||||
msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
|
||||
# mass_mailing, post without notify: notify specific partners
|
||||
if mass_mail_mode and not wizard.notify and post_values['partner_ids']:
|
||||
self.pool.get('mail.notification')._notify(cr, uid, msg_id, post_values['partner_ids'], context=context)
|
||||
context = dict(context,
|
||||
mail_notify_force_send=False, # do not send emails directly but use the queue instead
|
||||
mail_create_nosubscribe=True) # add context key to avoid subscribing the author
|
||||
active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
|
||||
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
|
||||
@@ -440,7 +440,7 @@
|
||||
<field name="model">mrp.bom</field>
|
||||
<field name="field_parent">child_complete_ids</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Bill of Materials" colors="blue:method">
|
||||
<tree string="Bill of Materials">
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="name" invisible="1"/>
|
||||
<field name="product_id"/>
|
||||
@@ -448,7 +448,6 @@
|
||||
<field name="product_uom" groups="product.group_uom"/>
|
||||
<field name="code"/>
|
||||
<field name="type"/>
|
||||
<field name="method"/>
|
||||
<field name="routing_id" groups="mrp.group_mrp_routings"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_stop"/>
|
||||
@@ -460,19 +459,18 @@
|
||||
<field name="model">mrp.bom</field>
|
||||
<field name="field_parent">child_complete_ids</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="BoM Structure" colors="blue:method">
|
||||
<tree string="BoM Structure">
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="name" groups="base.group_no_one"/>
|
||||
<field name="code"/>
|
||||
<field name="product_id"/>
|
||||
<field name="bom_id"/>
|
||||
<field name="bom_id" groups="product.group_mrp_properties"/>
|
||||
<field name="product_qty"/>
|
||||
<field name="product_uom" groups="product.group_uom"/>
|
||||
<field name="type"/>
|
||||
<field name="method"/>
|
||||
<field name="routing_id" groups="mrp.group_mrp_routings"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_stop"/>
|
||||
<field name="date_start" groups="product.group_mrp_properties"/>
|
||||
<field name="date_stop" groups="product.group_mrp_properties"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -545,6 +543,7 @@
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">mrp.bom</field>
|
||||
<field name="domain">[('bom_id', '=',active_ids)]</field>
|
||||
<field name="view_type">tree</field>
|
||||
<field name="view_id" ref="mrp_bom_tree_view"/>
|
||||
</record>
|
||||
<record id="ir_BOM_structure" model="ir.values">
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
// hide()s
|
||||
|
||||
function openerp_pos_screens(instance, module){ //module is instance.point_of_sale
|
||||
var QWeb = instance.web.qweb;
|
||||
var QWeb = instance.web.qweb,
|
||||
_t = instance.web._t;
|
||||
|
||||
module.ScreenSelector = instance.web.Class.extend({
|
||||
init: function(options){
|
||||
@@ -264,7 +265,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
// we add the help button by default. we do this because the buttons are cleared on each refresh so that
|
||||
// the button stay local to each screen
|
||||
this.pos_widget.left_action_bar.add_new_button({
|
||||
label: 'help',
|
||||
label: _t('Help'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/help.png',
|
||||
click: function(){ self.help_button_action(); },
|
||||
});
|
||||
@@ -454,7 +455,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
},500);
|
||||
|
||||
this.add_action_button({
|
||||
label: 'back',
|
||||
label: _t('Back'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
|
||||
click: function(){
|
||||
clearInterval(this.intervalID);
|
||||
@@ -483,7 +484,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
|
||||
|
||||
this.add_action_button({
|
||||
label: 'back',
|
||||
label: _t('Back'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
|
||||
click: function(){
|
||||
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
|
||||
@@ -491,7 +492,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
});
|
||||
|
||||
this.validate_button = this.add_action_button({
|
||||
label: 'Validate',
|
||||
label: _t('Validate'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
|
||||
click: function(){
|
||||
self.order_product();
|
||||
@@ -674,7 +675,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
}
|
||||
|
||||
this.add_action_button({
|
||||
label: 'back',
|
||||
label: _t('Back'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
|
||||
click: function(){
|
||||
self.queue.schedule(self.cancel);
|
||||
@@ -714,7 +715,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
var self = this;
|
||||
|
||||
this.add_action_button({
|
||||
label: 'help',
|
||||
label: _t('Help'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/help.png',
|
||||
click: function(){
|
||||
$('.goodbye-message').css({opacity:1}).hide();
|
||||
@@ -768,7 +769,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
|
||||
if(this.pos_widget.screen_selector.current_mode === 'client'){
|
||||
this.add_action_button({
|
||||
label: 'pay',
|
||||
label: _t('Pay'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
|
||||
click: function(){
|
||||
self.pos_widget.screen_selector.set_current_screen(self.client_next_screen);
|
||||
@@ -808,13 +809,13 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
var self = this;
|
||||
|
||||
this.add_action_button({
|
||||
label: 'Print',
|
||||
label: _t('Print'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
|
||||
click: function(){ self.print(); },
|
||||
});
|
||||
|
||||
this.add_action_button({
|
||||
label: 'Next Order',
|
||||
label: _t('Next Order'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
|
||||
click: function() { self.finishOrder(); },
|
||||
});
|
||||
@@ -870,7 +871,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
this.set_numpad_state(this.pos_widget.numpad.state);
|
||||
|
||||
this.back_button = this.add_action_button({
|
||||
label: 'Back',
|
||||
label: _t('Back'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
|
||||
click: function(){
|
||||
self.pos_widget.screen_selector.set_current_screen(self.back_screen);
|
||||
@@ -878,7 +879,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
});
|
||||
|
||||
this.validate_button = this.add_action_button({
|
||||
label: 'Validate',
|
||||
label: _t('Validate'),
|
||||
name: 'validation',
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
|
||||
click: function(){
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
function openerp_pos_widgets(instance, module){ //module is instance.point_of_sale
|
||||
var QWeb = instance.web.qweb;
|
||||
var QWeb = instance.web.qweb,
|
||||
_t = instance.web._t;
|
||||
|
||||
// The ImageCache is used to hide the latency of the application cache on-disk access in chrome
|
||||
// that causes annoying flickering on product pictures. Why the hell a simple access to
|
||||
@@ -980,13 +981,13 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
this.onscreen_keyboard.appendTo($(".point-of-sale #content"));
|
||||
|
||||
this.close_button = new module.HeaderButtonWidget(this,{
|
||||
label:'Close',
|
||||
label: _t('Close'),
|
||||
action: function(){ self.try_close(); },
|
||||
});
|
||||
this.close_button.appendTo(this.$('#rightheader'));
|
||||
|
||||
this.client_button = new module.HeaderButtonWidget(this,{
|
||||
label:'Self-Checkout',
|
||||
label: _t('Self-Checkout'),
|
||||
action: function(){ self.screen_selector.set_user_mode('client'); },
|
||||
});
|
||||
this.client_button.appendTo(this.$('#rightheader'));
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" eval="False"/>
|
||||
<field name="arch" type="xml">
|
||||
<kanban>
|
||||
<kanban create="false">
|
||||
<field name="last_login"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
|
||||
@@ -256,7 +256,7 @@
|
||||
<field name="search_view_id" ref="warehouse_orderpoint_search" />
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add a reordering rules.
|
||||
Click to add a reordering rule.
|
||||
</p><p>You can define your minimum stock rules, so that OpenERP will automatically create draft manufacturing orders or request for quotations according to the stock level. Once the virtual stock of a product (= stock on hand minus all confirmed orders and reservations) is below the minimum quantity, OpenERP will generate a procurement request to increase the stock up to the maximum quantity.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Procurement Request" version="7.0">
|
||||
<p class="oe_gray">
|
||||
Fill is this for to launch a procurement request for this
|
||||
Use this assistant to generate a procurement request for this
|
||||
product. According to the product configuration, this may
|
||||
trigger a draft purchase order, a manufacturing order or
|
||||
a new task.
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<page string="Sales & Purchases" position="inside">
|
||||
<group>
|
||||
<group name="pricelists" groups="product.group_sale_pricelist" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}">
|
||||
<field name="property_product_pricelist"/>
|
||||
<group name="pricelists" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}">
|
||||
<field name="property_product_pricelist" groups="product.group_sale_pricelist"/>
|
||||
</group>
|
||||
<div name="parent_pricelists" groups="product.group_sale_pricelist" attrs="{'invisible': ['|',('is_company','=',True),('parent_id','=',False)]}">
|
||||
<p>Pricelists are managed on <button name="open_commercial_entity" type="object" string="the parent company" class="oe_link"/></p>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Resource: product.uom.categ
|
||||
-->
|
||||
<record id="product_category_all" model="product.category">
|
||||
<field name="name">All products</field>
|
||||
<field name="name">All</field>
|
||||
</record>
|
||||
<record id="product_category_1" model="product.category">
|
||||
<field name="parent_id" ref="product_category_all"/>
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:param name="pmaxChars" as="xs:integer" select="80"/>
|
||||
|
||||
<xsl:template match="lot-line" mode="story">
|
||||
<blockTable style="mytable" colWidths="2.8cm,5.4cm">
|
||||
<tr>
|
||||
@@ -75,7 +77,7 @@
|
||||
<barCode><xsl:value-of select="ean13" /></barCode>
|
||||
</td>
|
||||
<td>
|
||||
<para style="nospace"><xsl:value-of select="product"/></para><xsl:text>, </xsl:text>
|
||||
<para style="nospace"><xsl:value-of select="substring(product, 1, pmaxChars)"/></para><xsl:text>, </xsl:text>
|
||||
<para style="nospace"><xsl:value-of select="variant"/></para>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -98,10 +98,10 @@ class sale_order_line(osv.osv):
|
||||
class account_invoice_line(osv.osv):
|
||||
_inherit = "account.invoice.line"
|
||||
|
||||
def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
|
||||
res = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit,currency_id, context=context, company_id=company_id)
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
|
||||
res = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit,currency_id, context=context, company_id=company_id)
|
||||
|
||||
def get_real_price(res_dict, product_id, qty, uom, pricelist):
|
||||
def get_real_price(res_dict, product_id, qty, uom_id, pricelist):
|
||||
item_obj = self.pool.get('product.pricelist.item')
|
||||
price_type_obj = self.pool.get('product.price.type')
|
||||
product_obj = self.pool.get('product.product')
|
||||
@@ -119,7 +119,7 @@ class account_invoice_line(osv.osv):
|
||||
product_read = product_obj.read(cr, uid, product_id, [field_name], context=context)
|
||||
|
||||
factor = 1.0
|
||||
if uom and uom != product.uom_id.id:
|
||||
if uom_id and uom_id != product.uom_id.id:
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
uom_data = product_uom_obj.browse(cr, uid, product.uom_id.id)
|
||||
factor = uom_data.factor
|
||||
@@ -137,18 +137,18 @@ class account_invoice_line(osv.osv):
|
||||
pricelist =partner_obj.browse(cr, uid, partner_id).property_product_pricelist_purchase.id
|
||||
if not pricelist:
|
||||
raise osv.except_osv(_('No Purchase Pricelist Found!'),_("You must first define a pricelist on the supplier form!"))
|
||||
price_unit_res = pricelist_obj.price_get(cr, uid, [pricelist], product.id, qty or 1.0, partner_id, {'uom': uom})
|
||||
price_unit_res = pricelist_obj.price_get(cr, uid, [pricelist], product.id, qty or 1.0, partner_id, {'uom': uom_id})
|
||||
price_unit = price_unit_res[pricelist]
|
||||
real_price = get_real_price(price_unit_res, product.id, qty, uom, pricelist)
|
||||
real_price = get_real_price(price_unit_res, product.id, qty, uom_id, pricelist)
|
||||
else:
|
||||
if partner_id:
|
||||
pricelist = partner_obj.browse(cr, uid, partner_id).property_product_pricelist.id
|
||||
if not pricelist:
|
||||
raise osv.except_osv(_('No Sale Pricelist Found!'),_("You must first define a pricelist on the customer form!"))
|
||||
price_unit_res = pricelist_obj.price_get(cr, uid, [pricelist], product.id, qty or 1.0, partner_id, {'uom': uom})
|
||||
price_unit_res = pricelist_obj.price_get(cr, uid, [pricelist], product.id, qty or 1.0, partner_id, {'uom': uom_id})
|
||||
price_unit = price_unit_res[pricelist]
|
||||
|
||||
real_price = get_real_price(price_unit_res, product.id, qty, uom, pricelist)
|
||||
real_price = get_real_price(price_unit_res, product.id, qty, uom_id, pricelist)
|
||||
if pricelist:
|
||||
pricelists=pricelist_obj.read(cr,uid,[pricelist],['visible_discount'])
|
||||
if(len(pricelists)>0 and pricelists[0]['visible_discount'] and real_price != 0):
|
||||
|
||||
@@ -29,6 +29,14 @@ class res_partner(osv.osv):
|
||||
'task_ids': fields.one2many('project.task', 'partner_id', 'Tasks'),
|
||||
}
|
||||
|
||||
def copy(self, cr, uid, record_id, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
|
||||
default['task_ids'] = []
|
||||
return super(res_partner, self).copy(
|
||||
cr, uid, record_id, default=default, context=context)
|
||||
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<field name="priority">36</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_product_pricelist" position="after">
|
||||
<field name="property_product_pricelist_purchase" />
|
||||
<field name="property_product_pricelist_purchase" groups="product.group_purchase_pricelist"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -186,7 +186,7 @@ class purchase_order(osv.osv):
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Destination Warehouse'),
|
||||
'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]} ),
|
||||
'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."),
|
||||
'currency_id': fields.related('pricelist_id', 'currency_id', type="many2one", relation="res.currency", string="Currency",readonly=True, required=True),
|
||||
'currency_id': fields.many2one('res.currency','Currency', readonly=True, required=True,states={'draft': [('readonly', False)],'sent': [('readonly', False)]}),
|
||||
'state': fields.selection(STATE_SELECTION, 'Status', readonly=True, help="The status of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' status. Then the order has to be confirmed by the user, the status switch to 'Confirmed'. Then the supplier must confirm the order to change the status to 'Approved'. When the purchase order is paid and received, the status becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the status becomes in exception.", select=True),
|
||||
'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines', states={'approved':[('readonly',True)],'done':[('readonly',True)]}),
|
||||
'validator' : fields.many2one('res.users', 'Validated by', readonly=True),
|
||||
@@ -237,6 +237,7 @@ class purchase_order(osv.osv):
|
||||
'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist_purchase.id,
|
||||
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.order', context=c),
|
||||
'journal_id': _get_journal,
|
||||
'currency_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
|
||||
}
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
|
||||
@@ -544,7 +545,7 @@ class purchase_order(osv.osv):
|
||||
'account_id': pay_acc_id,
|
||||
'type': 'in_invoice',
|
||||
'partner_id': order.partner_id.id,
|
||||
'currency_id': order.pricelist_id.currency_id.id,
|
||||
'currency_id': order.currency_id.id,
|
||||
'journal_id': len(journal_ids) and journal_ids[0] or False,
|
||||
'invoice_line': [(6, 0, inv_lines)],
|
||||
'origin': order.name,
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id)" context="{'search_default_supplier':1,'default_supplier':1,'default_customer':0}" domain="[('supplier','=',True)]"/>
|
||||
<field name="partner_ref"/>
|
||||
<field domain="[('type','=','purchase')]" name="pricelist_id" groups="product.group_purchase_pricelist" on_change="onchange_pricelist(pricelist_id, context)"/>
|
||||
<field name="currency_id" invisible='1'/>
|
||||
<field name="currency_id" groups="base.group_multi_currency"/>
|
||||
<field name="journal_id" invisible='1'/>
|
||||
</group>
|
||||
<group>
|
||||
|
||||
@@ -70,7 +70,7 @@ class stock_picking(osv.osv):
|
||||
|
||||
def get_currency_id(self, cursor, user, picking):
|
||||
if picking.purchase_id:
|
||||
return picking.purchase_id.pricelist_id.currency_id.id
|
||||
return picking.purchase_id.currency_id.id
|
||||
else:
|
||||
return super(stock_picking, self).get_currency_id(cursor, user, picking)
|
||||
|
||||
@@ -129,7 +129,7 @@ class stock_partial_picking(osv.osv_memory):
|
||||
def _product_cost_for_average_update(self, cr, uid, move):
|
||||
if move.picking_id.purchase_id:
|
||||
return {'cost': move.purchase_line_id.price_unit,
|
||||
'currency': move.picking_id.purchase_id.pricelist_id.currency_id.id}
|
||||
'currency': move.picking_id.purchase_id.currency_id.id}
|
||||
return super(stock_partial_picking, self)._product_cost_for_average_update(cr, uid, move)
|
||||
|
||||
# Redefinition of the new field in order to update the model stock.picking.in in the orm
|
||||
|
||||
@@ -65,7 +65,9 @@
|
||||
<field name="context">{"default_type": "in", "contact_display": "partner_address", "search_default_done": 1, "search_default_to_invoice": 1}</field>
|
||||
<field name="search_view_id" ref="stock.view_picking_in_search"/>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new incoming shipment.
|
||||
</p><p>
|
||||
Here you can track all the product receptions of purchase
|
||||
orders where the invoicing is "Based on Incoming Shipments",
|
||||
and for which you have not received a supplier invoice yet.
|
||||
|
||||
@@ -83,7 +83,7 @@ class purchase_line_invoice(osv.osv_memory):
|
||||
'account_id': a,
|
||||
'partner_id': partner.id,
|
||||
'invoice_line': [(6,0,lines_ids)],
|
||||
'currency_id' : orders[0].pricelist_id.currency_id.id,
|
||||
'currency_id' : orders[0].currency_id.id,
|
||||
'comment': multiple_order_invoice_notes(orders),
|
||||
'payment_term': orders[0].payment_term_id.id,
|
||||
'fiscal_position': partner.property_account_position.id
|
||||
|
||||
@@ -242,8 +242,13 @@ class WebKitParser(report_sxw):
|
||||
def translate_call(self, src):
|
||||
"""Translate String."""
|
||||
ir_translation = self.pool['ir.translation']
|
||||
name = self.tmpl and 'addons/' + self.tmpl or None
|
||||
res = ir_translation._get_source(self.parser_instance.cr, self.parser_instance.uid,
|
||||
None, 'report', self.parser_instance.localcontext.get('lang', 'en_US'), src)
|
||||
name, 'report', self.parser_instance.localcontext.get('lang', 'en_US'), src)
|
||||
if res == src:
|
||||
# no translation defined, fallback on None (backward compatibility)
|
||||
res = ir_translation._get_source(self.parser_instance.cr, self.parser_instance.uid,
|
||||
None, 'report', self.parser_instance.localcontext.get('lang', 'en_US'), src)
|
||||
if not res :
|
||||
return src
|
||||
return res
|
||||
|
||||
@@ -90,7 +90,11 @@
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" eval="False"/>
|
||||
<field name="search_view_id" ref="view_resource_calendar_search"/>
|
||||
<field name="help">Define working hours and time table that could be scheduled to your project members</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Define working hours and time table that could be scheduled to your project members
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_resource_calendar_attendance_tree" model="ir.ui.view">
|
||||
|
||||
+1
-1
@@ -697,7 +697,7 @@ class sale_order_line(osv.osv):
|
||||
'order_id': fields.many2one('sale.order', 'Order Reference', required=True, ondelete='cascade', select=True, readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'name': fields.text('Description', required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sales order lines."),
|
||||
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True),
|
||||
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
|
||||
'invoiced': fields.function(_fnct_line_invoiced, string='Invoiced', type='boolean',
|
||||
store={
|
||||
|
||||
@@ -31,7 +31,7 @@ class sale_advance_payment_inv(osv.osv_memory):
|
||||
[('all', 'Invoice the whole sales order'), ('percentage','Percentage'), ('fixed','Fixed price (deposit)'),
|
||||
('lines', 'Some order lines')],
|
||||
'What do you want to invoice?', required=True,
|
||||
help="""Use All to create the final invoice.
|
||||
help="""Use Invoice the whole sale order to create the final invoice.
|
||||
Use Percentage to invoice a percentage of the total amount.
|
||||
Use Fixed Price to invoice a specific amound in advance.
|
||||
Use Some Order Lines to invoice a selection of the sales order lines."""),
|
||||
|
||||
@@ -78,11 +78,13 @@ class crm_make_sale(osv.osv_memory):
|
||||
['default', 'invoice', 'delivery', 'contact'])
|
||||
pricelist = partner.property_product_pricelist.id
|
||||
fpos = partner.property_account_position and partner.property_account_position.id or False
|
||||
payment_term = partner.property_payment_term and partner.property_payment_term.id or False
|
||||
new_ids = []
|
||||
for case in case_obj.browse(cr, uid, data, context=context):
|
||||
if not partner and case.partner_id:
|
||||
partner = case.partner_id
|
||||
fpos = partner.property_account_position and partner.property_account_position.id or False
|
||||
payment_term = partner.property_payment_term and partner.property_payment_term.id or False
|
||||
partner_addr = partner_obj.address_get(cr, uid, [partner.id],
|
||||
['default', 'invoice', 'delivery', 'contact'])
|
||||
pricelist = partner.property_product_pricelist.id
|
||||
@@ -100,6 +102,7 @@ class crm_make_sale(osv.osv_memory):
|
||||
'partner_shipping_id': partner_addr['delivery'],
|
||||
'date_order': fields.date.context_today(self,cr,uid,context=context),
|
||||
'fiscal_position': fpos,
|
||||
'payment_term':payment_term,
|
||||
}
|
||||
if partner.id:
|
||||
vals['user_id'] = partner.user_id and partner.user_id.id or uid
|
||||
|
||||
@@ -587,9 +587,6 @@ class sale_order_line(osv.osv):
|
||||
return res
|
||||
|
||||
#update of result obtained in super function
|
||||
res_packing = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context)
|
||||
res['value'].update(res_packing.get('value', {}))
|
||||
warning_msgs = res_packing.get('warning') and res_packing['warning']['message'] or ''
|
||||
product_obj = product_obj.browse(cr, uid, product, context=context)
|
||||
res['value']['delay'] = (product_obj.sale_delay or 0.0)
|
||||
res['value']['type'] = product_obj.procure_method
|
||||
@@ -602,6 +599,11 @@ class sale_order_line(osv.osv):
|
||||
uom = False
|
||||
if not uom2:
|
||||
uom2 = product_obj.uom_id
|
||||
|
||||
# Calling product_packaging_change function after updating UoM
|
||||
res_packing = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context)
|
||||
res['value'].update(res_packing.get('value', {}))
|
||||
warning_msgs = res_packing.get('warning') and res_packing['warning']['message'] or ''
|
||||
compare_qty = float_compare(product_obj.virtual_available * uom2.factor, qty * product_obj.uom_id.factor, precision_rounding=product_obj.uom_id.rounding)
|
||||
if (product_obj.type=='product') and int(compare_qty) == -1 \
|
||||
and (product_obj.procure_method=='make_to_stock'):
|
||||
|
||||
@@ -14,7 +14,7 @@ openerp.share = function(session) {
|
||||
else rec_name = '';
|
||||
session.web.pyeval.eval_domains_and_contexts({
|
||||
domains: [domain],
|
||||
contexts: [view.dataset.context]
|
||||
contexts: [Share.get_context()]
|
||||
}).done(function (result) {
|
||||
Share.create({
|
||||
name: action.name,
|
||||
@@ -25,7 +25,7 @@ openerp.share = function(session) {
|
||||
view_type: view.fields_view.type,
|
||||
invite: invite || false,
|
||||
}).done(function(share_id) {
|
||||
var step1 = Share.call('go_step_1', [[share_id]]).done(function(result) {
|
||||
var step1 = Share.call('go_step_1', [[share_id], Share.get_context()]).done(function(result) {
|
||||
var action = result;
|
||||
self.do_action(action);
|
||||
});
|
||||
|
||||
@@ -258,7 +258,9 @@
|
||||
<para style="terp_default_Centre_9">[[ (move_lines.prodlot_id and move_lines.prodlot_id.name) or '' ]]</para>
|
||||
</td>
|
||||
<td>
|
||||
<para style="terp_default_9">[[ move_lines.state ]]</para>
|
||||
<para style="terp_default_9">Waiting Availability[[ move_lines.state == 'confirmed' and ' ' or removeParentNode('para') ]]</para>
|
||||
<para style="terp_default_9">Done[[ move_lines.state == 'done' and ' ' or removeParentNode('para') ]]</para>
|
||||
<para style="terp_default_9">Available[[ move_lines.state == 'assigned' and ' ' or removeParentNode('para') ]]</para>
|
||||
</td>
|
||||
<td>
|
||||
<para style="terp_default_Right_9">[[ (move_lines.location_id and move_lines.location_id.name) or '' ]] </para>
|
||||
|
||||
@@ -1637,7 +1637,7 @@ class stock_move(osv.osv):
|
||||
"* Waiting Availability: This state is reached when the procurement resolution is not straight forward. It may need the scheduler to run, a component to me manufactured...\n"\
|
||||
"* Available: When products are reserved, it is set to \'Available\'.\n"\
|
||||
"* Done: When the shipment is processed, the state is \'Done\'."),
|
||||
'price_unit': fields.float('Unit Price', digits_compute= dp.get_precision('Account'), help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used)"),
|
||||
'price_unit': fields.float('Unit Price', digits_compute= dp.get_precision('Product Price'), help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used)"),
|
||||
'price_currency_id': fields.many2one('res.currency', 'Currency for average price', help="Technical field used to record the currency chosen by the user during a picking confirmation (when average price costing method is used)"),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
|
||||
'backorder_id': fields.related('picking_id','backorder_id',type='many2one', relation="stock.picking", string="Back Order of", select=True),
|
||||
@@ -2192,7 +2192,7 @@ class stock_move(osv.osv):
|
||||
if move.picking_id:
|
||||
pickings.add(move.picking_id.id)
|
||||
if move.move_dest_id and move.move_dest_id.state == 'waiting':
|
||||
self.write(cr, uid, [move.move_dest_id.id], {'state': 'assigned'})
|
||||
self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'})
|
||||
if context.get('call_unlink',False) and move.move_dest_id.picking_id:
|
||||
wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
|
||||
self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False})
|
||||
@@ -2412,8 +2412,8 @@ class stock_move(osv.osv):
|
||||
# or if it's the same as that of the secondary amount being posted.
|
||||
account_obj = self.pool.get('account.account')
|
||||
src_acct, dest_acct = account_obj.browse(cr, uid, [src_account_id, dest_account_id], context=context)
|
||||
src_main_currency_id = src_acct.currency_id and src_acct.currency_id.id or src_acct.company_id.currency_id.id
|
||||
dest_main_currency_id = dest_acct.currency_id and dest_acct.currency_id.id or dest_acct.company_id.currency_id.id
|
||||
src_main_currency_id = src_acct.company_id.currency_id.id
|
||||
dest_main_currency_id = dest_acct.company_id.currency_id.id
|
||||
cur_obj = self.pool.get('res.currency')
|
||||
if reference_currency_id != src_main_currency_id:
|
||||
# fix credit line:
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
import time
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class stock_partial_move_line(osv.osv_memory):
|
||||
_inherit = "stock.partial.picking.line"
|
||||
@@ -67,6 +68,8 @@ class stock_partial_move(osv.osv_memory):
|
||||
}
|
||||
moves_ids = []
|
||||
for move in partial.move_ids:
|
||||
if not move.move_id:
|
||||
raise osv.except_osv(_('Warning !'), _("You have manually created product lines, please delete them to proceed"))
|
||||
move_id = move.move_id.id
|
||||
partial_data['move%s' % (move_id)] = {
|
||||
'product_id': move.product_id.id,
|
||||
|
||||
@@ -180,6 +180,8 @@ class stock_return_picking(osv.osv_memory):
|
||||
for v in val_id:
|
||||
data_get = data_obj.browse(cr, uid, v, context=context)
|
||||
mov_id = data_get.move_id.id
|
||||
if not mov_id:
|
||||
raise osv.except_osv(_('Warning !'), _("You have manually created product lines, please delete them to proceed"))
|
||||
new_qty = data_get.quantity
|
||||
move = move_obj.browse(cr, uid, mov_id, context=context)
|
||||
new_location = move.location_dest_id.id
|
||||
|
||||
@@ -498,7 +498,7 @@ class survey_question(osv.osv):
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
minimum_ans = 0
|
||||
maximum_ans = 0
|
||||
page = self.pool.get('survey.page').browse(cr, uid, vals['page_id'], context=context).title
|
||||
page = self.pool.get('survey.page').browse(cr, uid, int(vals.get('page_id', 0)), context=context).title
|
||||
if vals.has_key('answer_choice_ids') and not len(vals['answer_choice_ids']):
|
||||
if vals.has_key('type') and vals['type'] not in ['descriptive_text', 'single_textbox', 'comment','table']:
|
||||
raise osv.except_osv(_('Warning!'),_('You must enter one or more answers for question "%s" of page %s .') % (vals['question'], page))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user