[MRG] merge with lp:openobject-addons

bzr revid: tpa@tinyerp.com-20130617100451-vwtgtfp4r523xsp3
This commit is contained in:
Turkesh Patel (Open ERP) 2013-06-17 15:34:51 +05:30
commit e2c343fc63
197 changed files with 7330 additions and 2472 deletions

View File

@ -648,10 +648,10 @@ class account_account(osv.osv):
if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
#Check for 'Closed' type
if old_type == 'closed' and new_type !='closed':
raise osv.except_osv(_('Warning !'), _("You cannot change the type of account from 'Closed' to any other type as it contains journal items!"))
raise osv.except_osv(_('Warning!'), _("You cannot change the type of account from 'Closed' to any other type as it contains journal items!"))
# Forbid to change an account type for restricted_groups as it contains journal items (or if one of its children does)
if (new_type in restricted_groups):
raise osv.except_osv(_('Warning !'), _("You cannot change the type of account to '%s' type as it contains journal items!") % (new_type,))
raise osv.except_osv(_('Warning!'), _("You cannot change the type of account to '%s' type as it contains journal items!") % (new_type,))
return True
@ -1015,14 +1015,14 @@ class account_period(osv.osv):
if not result:
result = self.search(cr, uid, args, context=context)
if not result:
raise osv.except_osv(_('Error !'), _('There is no period defined for this date: %s.\nPlease create one.')%dt)
raise osv.except_osv(_('Error!'), _('There is no period defined for this date: %s.\nPlease create one.')%dt)
return result
def action_draft(self, cr, uid, ids, *args):
mode = 'draft'
for period in self.browse(cr, uid, ids):
if period.fiscalyear_id.state == 'done':
raise osv.except_osv(_('Warning !'), _('You can not re-open a period which belongs to closed fiscal year'))
raise osv.except_osv(_('Warning!'), _('You can not re-open a period which belongs to closed fiscal year'))
cr.execute('update account_journal_period set state=%s where period_id in %s', (mode, tuple(ids),))
cr.execute('update account_period set state=%s where id in %s', (mode, tuple(ids),))
return True
@ -2317,7 +2317,7 @@ class account_model(osv.osv):
try:
entry['name'] = model.name%{'year': move_date.strftime('%Y'), 'month': move_date.strftime('%m'), 'date': move_date.strftime('%Y-%m')}
except:
raise osv.except_osv(_('Wrong model !'), _('You have a wrong expression "%(...)s" in your model !'))
raise osv.except_osv(_('Wrong Model!'), _('You have a wrong expression "%(...)s" in your model!'))
move_id = account_move_obj.create(cr, uid, {
'ref': entry['name'],
'period_id': period_id,
@ -2329,7 +2329,7 @@ class account_model(osv.osv):
analytic_account_id = False
if line.analytic_account_id:
if not model.journal_id.analytic_journal_id:
raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal on the '%s' journal!") % (model.journal_id.name,))
raise osv.except_osv(_('No Analytic Journal!'),_("You have to define an analytic journal on the '%s' journal!") % (model.journal_id.name,))
analytic_account_id = line.analytic_account_id.id
val = {
'move_id': move_id,

View File

@ -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,

View File

@ -420,7 +420,7 @@ class account_bank_statement(osv.osv):
for st_line in st.line_ids:
if st_line.analytic_account_id:
if not st.journal_id.analytic_journal_id:
raise osv.except_osv(_('No Analytic Journal !'),_("You have to assign an analytic journal on the '%s' journal!") % (st.journal_id.name,))
raise osv.except_osv(_('No Analytic Journal!'),_("You have to assign an analytic journal on the '%s' journal!") % (st.journal_id.name,))
if not st_line.amount:
continue
st_line_number = self.get_next_st_line_number(cr, uid, st_number, st_line, context)

View File

@ -252,7 +252,7 @@ class account_cash_statement(osv.osv):
for statement in statement_pool.browse(cr, uid, ids, context=context):
vals = {}
if not self._user_allow(cr, uid, statement.id, context=context):
raise osv.except_osv(_('Error!'), (_('You do not have rights to open this %s journal !') % (statement.journal_id.name, )))
raise osv.except_osv(_('Error!'), (_('You do not have rights to open this %s journal!') % (statement.journal_id.name, )))
if statement.name and statement.name == '/':
c = {'fiscalyear_id': statement.period_id.fiscalyear_id.id}

View File

@ -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):
@ -69,7 +72,7 @@ class account_invoice(osv.osv):
tt = type2journal.get(type_inv, 'sale')
result = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=',tt)], context=context)
if not result:
raise osv.except_osv(_('No Analytic Journal !'),_("You must define an analytic journal of type '%s'!") % (tt,))
raise osv.except_osv(_('No Analytic Journal!'),_("You must define an analytic journal of type '%s'!") % (tt,))
return result[0]
def _get_type(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')
@ -630,18 +637,17 @@ class account_invoice(osv.osv):
obj_l = account_obj.browse(cr, uid, inv_line[2]['account_id'])
if obj_l.company_id.id != company_id:
raise osv.except_osv(_('Configuration Error!'),
_('Invoice line account\'s company and invoice\'s compnay does not match.'))
_('Invoice line account\'s company and invoice\'s company does not match.'))
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, [])
@ -786,7 +797,7 @@ class account_invoice(osv.osv):
else:
ref = self._convert_ref(cr, uid, inv.number)
if not inv.journal_id.analytic_journal_id:
raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal on the '%s' journal!") % (inv.journal_id.name,))
raise osv.except_osv(_('No Analytic Journal!'),_("You have to define an analytic journal on the '%s' journal!") % (inv.journal_id.name,))
il['analytic_lines'] = [(0,0, {
'name': il['name'],
'date': inv['date_invoice'],
@ -912,7 +923,7 @@ class account_invoice(osv.osv):
if not inv.journal_id.sequence_id:
raise osv.except_osv(_('Error!'), _('Please define sequence on the journal related to this invoice.'))
if not inv.invoice_line:
raise osv.except_osv(_('No Invoice Lines !'), _('Please create some invoice lines.'))
raise osv.except_osv(_('No Invoice Lines!'), _('Please create some invoice lines.'))
if inv.move_id:
continue
@ -933,7 +944,7 @@ class account_invoice(osv.osv):
group_check_total = self.pool.get('res.groups').browse(cr, uid, group_check_total_id, context=context)
if group_check_total and uid in [x.id for x in group_check_total.users]:
if (inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0)):
raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe encoded total does not match the computed total.'))
raise osv.except_osv(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.'))
if inv.payment_term:
total_fixed = total_percent = 0
@ -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:
@ -1475,7 +1486,7 @@ class account_invoice_line(osv.osv):
context = dict(context)
context.update({'company_id': company_id, 'force_company': company_id})
if not partner_id:
raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
raise osv.except_osv(_('No Partner Defined!'),_("You must first select a partner!") )
if not product:
if type in ('in_invoice', 'in_refund'):
return {'value': {}, 'domain':{'product_uom':[]}}

View File

@ -192,7 +192,7 @@ class account_move_line(osv.osv):
for obj_line in self.browse(cr, uid, ids, context=context):
if obj_line.analytic_account_id:
if not obj_line.journal_id.analytic_journal_id:
raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal on the '%s' journal!") % (obj_line.journal_id.name, ))
raise osv.except_osv(_('No Analytic Journal!'),_("You have to define an analytic journal on the '%s' journal!") % (obj_line.journal_id.name, ))
vals_line = self._prepare_analytic_line(cr, uid, obj_line, context=context)
acc_ana_line_obj.create(cr, uid, vals_line)
return True
@ -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:
@ -1101,7 +1101,7 @@ class account_move_line(osv.osv):
period = period_obj.browse(cr, uid, period_id, context=context)
for (state,) in result:
if state == 'done':
raise osv.except_osv(_('Error !'), _('You can not add/modify entries in a closed period %s of journal %s.' % (period.name,journal.name)))
raise osv.except_osv(_('Error!'), _('You can not add/modify entries in a closed period %s of journal %s.' % (period.name,journal.name)))
if not result:
jour_period_obj.create(cr, uid, {
'name': (journal.code or journal.name)+':'+(period.name or ''),
@ -1181,7 +1181,7 @@ class account_move_line(osv.osv):
move_id = move_obj.create(cr, uid, v, context)
vals['move_id'] = move_id
else:
raise osv.except_osv(_('No piece number !'), _('Cannot create an automatic sequence for this piece.\nPut a sequence in the journal definition for automatic numbering or create a sequence manually for this piece.'))
raise osv.except_osv(_('No Piece Number!'), _('Cannot create an automatic sequence for this piece.\nPut a sequence in the journal definition for automatic numbering or create a sequence manually for this piece.'))
ok = not (journal.type_control_ids or journal.account_control_ids)
if ('account_id' in vals):
account = account_obj.browse(cr, uid, vals['account_id'], context=context)

View File

@ -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="[
'&amp;',
'|',('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', '&lt;&gt;', 'view')]"/>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -111,7 +111,7 @@ class account_installer(osv.osv_memory):
def check_unconfigured_cmp(self, cr, uid, context=None):
""" check if there are still unconfigured companies """
if not self.get_unconfigured_cmp(cr, uid, context=context):
raise osv.except_osv(_('No unconfigured company !'), _("There is currently no company without chart of account. The wizard will therefore not be executed."))
raise osv.except_osv(_('No Unconfigured Company!'), _("There is currently no company without chart of account. The wizard will therefore not be executed."))
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
if context is None: context = {}

View File

@ -61,7 +61,7 @@
<field name="name">partner.view.buttons</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="priority" eval="10"/>
<field name="priority" eval="20"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button type="action" string="Invoices"
@ -96,7 +96,7 @@
<field name="priority">2</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<page string="History" position="before" version="7.0">
<page name="sales_purchases" position="after" version="7.0">
<page string="Accounting" col="4" name="accounting" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}">
<group>
<group>

View File

@ -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>

View File

@ -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>

View File

@ -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):

View File

@ -57,7 +57,7 @@ class validate_account_move_lines(osv.osv_memory):
move_ids.append(line.move_id.id)
move_ids = list(set(move_ids))
if not move_ids:
raise osv.except_osv(_('Warning!'), _('Selected Entry Lines does not have any account move enties in draft state.'))
raise osv.except_osv(_('Warning!'), _('Selected Entry Lines does not have any account move entries in draft state.'))
obj_move.button_validate(cr, uid, move_ids, context)
return {'type': 'ir.actions.act_window_close'}

View File

@ -653,7 +653,7 @@ class account_analytic_account(osv.osv):
fpos_obj = self.pool.get('account.fiscal.position')
if not contract.partner_id:
raise osv.except_osv(_('No Customer Defined !'),_("You must first select a Customer for Contract %s!") % contract.name )
raise osv.except_osv(_('No Customer Defined!'),_("You must first select a Customer for Contract %s!") % contract.name )
fpos = contract.partner_id.property_account_position.id or False
journal_ids = journal_obj.search(cr, uid, [('type', '=','sale'),('company_id', '=', contract.company_id.id or False)], limit=1)

View File

@ -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})

View File

@ -332,7 +332,7 @@ class account_move_line(osv.osv):
for line in self.browse(cr, uid, ids, context=context):
if line.analytics_id:
if not line.journal_id.analytic_journal_id:
raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal on the '%s' journal.") % (line.journal_id.name,))
raise osv.except_osv(_('No Analytic Journal!'),_("You have to define an analytic journal on the '%s' journal.") % (line.journal_id.name,))
toremove = analytic_line_obj.search(cr, uid, [('move_id','=',line.id)], context=context)
if toremove:
@ -471,7 +471,7 @@ class account_bank_statement(osv.osv):
for st_line in st.line_ids:
if st_line.analytics_id:
if not st.journal_id.analytic_journal_id:
raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal on the '%s' journal.") % (st.journal_id.name,))
raise osv.except_osv(_('No Analytic Journal!'),_("You have to define an analytic journal on the '%s' journal.") % (st.journal_id.name,))
if not st_line.amount:
continue
return True

View File

@ -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>

View File

@ -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'):

View File

@ -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>

View File

@ -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

View File

@ -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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 access_account_followup_followup_line_manager account_followup.followup.line.manager model_account_followup_followup_line account.group_account_manager 1 1 1 1
4 access_account_followup_followup_accountant account_followup.followup user model_account_followup_followup account.group_account_invoice 1 0 0 0
5 access_account_followup_followup_manager account_followup.followup.manager model_account_followup_followup account.group_account_manager 1 1 1 1
6 access_account_followup_stat_invoice account_followup.stat.invoice model_account_followup_stat account.group_account_invoice 1 1 1 0 1 0
7 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 0 1 0

View File

@ -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)

View File

@ -1622,7 +1622,7 @@ class account_bank_statement(osv.osv):
for bk_st in self.browse(cr, uid, ids, context=context):
if vals.get('journal_id') and bk_st.line_ids:
if any([x.voucher_id and True or False for x in bk_st.line_ids]):
raise osv.except_osv(_('Unable to change journal !'), _('You can not change the journal as you already reconciled some statement lines!'))
raise osv.except_osv(_('Unable to Change Journal!'), _('You can not change the journal as you already reconciled some statement lines!'))
return super(account_bank_statement, self).write(cr, uid, ids, vals, context=context)

View File

@ -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 "&#1052;&#1077;&#1089;&#1103;&#1094;"
#. 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 ""

View File

@ -157,7 +157,7 @@ class account_analytic_account(osv.osv):
for account in self.browse(cr, uid, ids, context=context):
if account.company_id:
if account.company_id.currency_id.id != value:
raise osv.except_osv(_('Error!'), _("If you set a company, the currency selected has to be the same as it's currency. \nYou can remove the company belonging, and thus change the currency, only on analytic account of type 'view'. This can be really usefull for consolidation purposes of several companies charts with different currencies, for example."))
raise osv.except_osv(_('Error!'), _("If you set a company, the currency selected has to be the same as it's currency. \nYou can remove the company belonging, and thus change the currency, only on analytic account of type 'view'. This can be really useful for consolidation purposes of several companies charts with different currencies, for example."))
if value:
return cr.execute("""update account_analytic_account set currency_id=%s where id=%s""", (value, account.id, ))

View File

@ -104,7 +104,7 @@ class hr_analytic_timesheet(osv.osv):
a = r.product_id.categ_id.property_account_expense_categ.id
if not a:
raise osv.except_osv(_('Error!'),
_('There is no expense account define ' \
_('There is no expense account defined ' \
'for this product: "%s" (id:%d)') % \
(r.product_id.name, r.product_id.id,))
# Compute based on pricetype
@ -133,7 +133,7 @@ class hr_analytic_timesheet(osv.osv):
a = r.product_id.categ_id.property_account_expense_categ.id
if not a:
raise osv.except_osv(_('Error!'),
_('There is no expense account define ' \
_('There is no expense account defined ' \
'for this product: "%s" (id:%d)') % \
(r.product_id.name, r.product_id.id,))
# Compute based on pricetype

View File

@ -86,11 +86,11 @@ class ir_model_fields_anonymization(osv.osv):
if context.get('manual'):
global_state = self._get_global_state(cr, uid, context=context)
if global_state == 'anonymized':
raise osv.except_osv('Error !', "The database is currently anonymized, you cannot create, modify or delete fields.")
raise osv.except_osv('Error!', "The database is currently anonymized, you cannot create, modify or delete fields.")
elif global_state == 'unstable':
msg = _("The database anonymization is currently in an unstable state. Some fields are anonymized," + \
" while some fields are not anonymized. You should try to solve this problem before trying to create, write or delete fields.")
raise osv.except_osv('Error !', msg)
raise osv.except_osv('Error!', msg)
return True
@ -357,7 +357,7 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory):
else:
msg = _("The database anonymization is currently in an unstable state. Some fields are anonymized," + \
" while some fields are not anonymized. You should try to solve this problem before trying to do anything else.")
raise osv.except_osv('Error !', msg)
raise osv.except_osv('Error!', msg)
res['arch'] = etree.tostring(eview)
@ -521,11 +521,11 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory):
# check that all the defined fields are in the 'anonymized' state
state = ir_model_fields_anonymization_model._get_global_state(cr, uid, context=context)
if state == 'clear':
raise osv.except_osv_('Error !', "The database is not currently anonymized, you cannot reverse the anonymization.")
raise osv.except_osv_('Error!', "The database is not currently anonymized, you cannot reverse the anonymization.")
elif state == 'unstable':
msg = _("The database anonymization is currently in an unstable state. Some fields are anonymized," + \
" while some fields are not anonymized. You should try to solve this problem before trying to do anything.")
raise osv.except_osv('Error !', msg)
raise osv.except_osv('Error!', msg)
wizards = self.browse(cr, uid, ids, context=context)
for wizard in wizards:

View File

@ -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 = {

View File

@ -60,7 +60,7 @@ class crm_meeting(base_state, osv.Model):
'categ_ids': fields.many2many('crm.meeting.type', 'meeting_category_rel',
'event_id', 'type_id', 'Tags'),
'attendee_ids': fields.many2many('calendar.attendee', 'meeting_attendee_rel',\
'event_id', 'attendee_id', 'Attendees', states={'done': [('readonly', True)]}),
'event_id', 'attendee_id', 'Invited People', states={'done': [('readonly', True)]}),
}
_defaults = {
'state': 'open',

View File

@ -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 Name Is a company Related company Address type Customer Supplier Street ZIP City State Country
2 Aurora Shelves 1 1 0 25 Pacific Road 95101 San José CA United States
3 Roger Martins 0 Aurora Shelves Invoice 1 0 27 Pacific Road 95102 San José CA United States
4 House Sales Direct 1 1 0 104 Saint Mary Avenue 94059 Redwood CA United States
5 Yvan Holiday 0 House Sales Direct Default 1 0 104 Saint Mary Avenue 94060 Redwood CA United States
6 Jack Unsworth 0 House Sales Direct Invoice 1 0 227 Jackson Road 94061 Redwood CA United States
7 Michael Mason 0 1 0 16 5th Avenue 94104 San Francisco CA United States
8 International Wood 1 1 0 748 White House Boulevard 20004 Washington DC United States
9 Sharon Pecker 0 International Wood Invoice 1 0 755 White House Boulevard 20005 Washington DC United States

View File

@ -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
1 Name Address type Street City Country Tags Supplier Customer Is a company Companies that refers to partner / Parent company
2 Wood y Wood Pecker Snow Street, 25 Kainuu Finland Supplier 1 0 1
3 Roger Pecker Default Snow Street, 27 Kainuu Finland Supplier 1 0 0 Wood y Wood Pecker
4 Sharon Pecker Delivery Snow Street, 28 Kainuu Finland Supplier 1 0 0 Wood y Wood Pecker
5 Thomas Pecker Contact Snow Street, 27 Kainuu Finland Supplier 1 0 0 Wood y Wood Pecker
6 Vicking Direct Atonium Street, 45a Brussels Belgium Supplier 1 0 1
7 Yvan Holiday Invoice Atonium Street, 45b Brussels Belgium Supplier 1 0 0 Vicking Direct
8 Jack Unsworth Contact Atonium Street, 45a Brussels Belgium Supplier 1 0 0 Vicking Direct

View File

@ -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>

View File

@ -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"/>

View File

@ -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)

View File

@ -104,7 +104,7 @@ class base_state(object):
if parent_id.change_responsible and parent_id.user_id:
data['user_id'] = parent_id.user_id.id
else:
raise osv.except_osv(_('Error !'), _('You can not escalate, you are already at the top level regarding your sales-team category.'))
raise osv.except_osv(_('Error!'), _('You can not escalate, you are already at the top level regarding your sales-team category.'))
self.write(cr, uid, [case.id], data, context=context)
case.case_escalate_send_note(parent_id, context=context)
return True

View File

@ -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(); }

View File

@ -2,19 +2,6 @@
<openerp>
<data>
<!-- Add section_id (Sales Team) to res.partner -->
<record id="view_partners_form_crm1" model="ir.ui.view">
<field name="name">view.res.partner.form.crm.inherited1</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field eval="18" name="priority"/>
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="section_id" completion="1" groups="base.group_multi_salesteams"/>
</field>
</field>
</record>
<!-- open meetings related to given partner -->
<record id="crm_meeting_partner" model="ir.actions.act_window">
<field name="name">Meetings</field>
@ -91,24 +78,31 @@
</field>
</record>
<record model="ir.ui.view" id="res_partner_view_buttons">
<field name="name">res.partner.view.buttons</field>
<!-- Add section_id (SalesTeam) and contextual button on partner form view -->
<record id="view_partners_form_crm1" model="ir.ui.view">
<field name="name">view.res.partner.form.crm.inherited1</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="priority" eval="10"/>
<field name="inherit_id" ref="base.view_partner_form"/>
<field eval="18" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button type="action"
string="Meetings"
name="%(base_calendar.action_crm_meeting)d"
context="{'search_default_partner_ids': active_id, 'default_partner_ids' : [active_id]}"/>
<button type="action" string="Calls"
name="%(crm.crm_case_categ_phone_incoming0)d"
context="{'search_default_partner_id': active_id, 'default_duration': 1.0}" />
<button type="action" string="Opportunities" attrs="{'invisible': [('customer', '=', False)]}"
name="%(crm.crm_case_category_act_oppor11)d" context="{'search_default_partner_id': active_id}"/>
</xpath>
<data>
<field name="user_id" position="after">
<field name="section_id" completion="1" groups="base.group_multi_salesteams"/>
</field>
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline" type="action" string="Opportunities"
attrs="{'invisible': [('customer', '=', False)]}"
name="%(crm.crm_case_category_act_oppor11)d"
context="{'search_default_partner_id': active_id}"/>
<button class="oe_inline" type="action"
string="Meetings"
name="%(base_calendar.action_crm_meeting)d"
context="{'search_default_partner_ids': active_id, 'default_partner_ids' : [active_id]}"/>
<button class="oe_inline" type="action" string="Calls"
name="%(crm.crm_case_categ_phone_incoming0)d"
context="{'search_default_partner_id': active_id, 'default_duration': 1.0}" />
</xpath>
</data>
</field>
</record>

View File

@ -86,7 +86,7 @@ class crm_lead2opportunity_partner(osv.osv_memory):
lead_obj = self.pool.get('crm.lead')
for lead in lead_obj.browse(cr, uid, context.get('active_ids', []), context=context):
if lead.state in ['done', 'cancel']:
raise osv.except_osv(_("Warning !"), _("Closed/Cancelled leads cannot be converted into opportunities."))
raise osv.except_osv(_("Warning!"), _("Closed/Cancelled leads cannot be converted into opportunities."))
return False
def _convert_opportunity(self, cr, uid, ids, vals, context=None):

View File

@ -42,14 +42,20 @@ automatically new claims based on incoming emails.
'security/ir.model.access.csv',
'report/crm_claim_report_view.xml',
'crm_claim_data.xml',
'res_partner_view.xml',
],
'demo': ['crm_claim_demo.xml'],
'test': ['test/process/claim.yml',
'test/ui/claim_demo.yml'
'test': [
'test/process/claim.yml',
'test/ui/claim_demo.yml'
],
'installable': True,
'auto_install': False,
'images': ['images/claim_categories.jpeg','images/claim_stages.jpeg','images/claims.jpeg'],
'images': [
'images/claim_categories.jpeg',
'images/claim_stages.jpeg',
'images/claims.jpeg'
],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -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">

View File

@ -216,35 +216,5 @@
</search>
</field>
</record>
<record id="view_claim_partner_info_form1" model="ir.ui.view">
<field name="name">res.partner.claim.info.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="priority">20</field>
<field name="arch" type="xml">
<xpath expr="//page[@name='page_history']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
<xpath expr="//page[@name='page_history']" position="inside">
<group name="grp_claim" string="Claims">
<field name="claims_ids" colspan="4" nolabel="1">
<tree string="Partners Claim" editable="bottom">
<field name="name"/>
</tree>
</field>
</group>
</xpath>
</field>
</record>
<act_window
context="{'search_default_partner_id': [active_id], 'default_partner_id': active_id}"
id="act_claim_partner"
name="Claims"
view_mode="tree,form"
res_model="crm.claim"
src_model="res.partner"/>
</data>
</openerp>

View File

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Partners inherited form -->
<record id="view_claim_res_partner_info_form" model="ir.ui.view">
<field name="name">res.partner.claim.info.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="priority" eval="50"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline" type="action"
name="%(crm_case_categ_claim0)d"
string="Claims"
context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}"
groups="base.group_sale_salesman" />
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-06-07 05:48+0000\n"
"X-Launchpad-Export-Date: 2013-06-08 05:52+0000\n"
"X-Generator: Launchpad (build 16667)\n"
#. module: crm_helpdesk

View File

@ -51,10 +51,10 @@ class sale_order(osv.osv):
for order in self.browse(cr, uid, ids, context=context):
grid_id = carrier_obj.grid_get(cr, uid, [order.carrier_id.id], order.partner_shipping_id.id)
if not grid_id:
raise osv.except_osv(_('No grid available !'), _('No grid matching for this carrier !'))
raise osv.except_osv(_('No Grid Available!'), _('No grid matching for this carrier!'))
if not order.state in ('draft'):
raise osv.except_osv(_('Order not in draft state !'), _('The order state have to be draft to add delivery lines.'))
raise osv.except_osv(_('Order not in Draft State!'), _('The order state have to be draft to add delivery lines.'))
grid = grid_obj.browse(cr, uid, grid_id, context=context)

View File

@ -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">

View File

@ -126,7 +126,7 @@ class edi(osv.AbstractModel):
module = edi_document.get('__import_module') or edi_document.get('__module')
assert module, 'a `__module` or `__import_module` attribute is required in each EDI document.'
if module != 'base' and not ir_module.search(cr, uid, [('name','=',module),('state','=','installed')]):
raise osv.except_osv(_('Missing application.'),
raise osv.except_osv(_('Missing Application.'),
_("The document you are trying to import requires the OpenERP `%s` application. "
"You can install it by connecting as the administrator and opening the configuration assistant.")%(module,))
model = edi_document.get('__import_model') or edi_document.get('__model')

View File

@ -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

View File

@ -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

View File

@ -21,15 +21,13 @@
from openerp.osv import fields, osv
class res_partner(osv.osv):
_inherit = 'res.partner'
_columns = {
'speaker': fields.boolean('Speaker', help="Check this box if this contact is a speaker."),
'event_ids': fields.one2many('event.event','main_speaker_id', readonly=True),
'event_registration_ids': fields.one2many('event.registration','partner_id', readonly=True),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -2,9 +2,8 @@
<openerp>
<data>
<!-- Partners inherited form -->
<record id="view_event_partner_info_form" model="ir.ui.view">
<!-- Partners inherited form -->
<record id="view_event_partner_info_form" model="ir.ui.view">
<field name="name">res.partner.event.info.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
@ -12,34 +11,8 @@
<field name="supplier" position="after">
<field name="speaker"/>
</field>
<xpath expr="//page[@name='page_history']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
<xpath expr="//page[@name='page_history']" position="inside">
<group name="grp_event" string="Events">
<field name="event_ids" colspan="4" nolabel="1">
<tree string="Events">
<field name="name" string="Event"/>
<field name="main_speaker_id"/>
</tree>
</field>
</group>
<group name="grp_registration" string="Registrations">
<field name="event_registration_ids" colspan="4" nolabel="1">
<tree string="Events Registration">
<field name="event_begin_date" string="Date"/>
<field name="event_id" />
<field name="nb_register"/>
<field name="state"/>
<button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object" icon="gtk-cancel"/>
<button name="button_reg_close" string="Close Registration" states="open" type="object" icon="gtk-close"/>
<button name="check_confirm" string="Confirm Registration" states="draft" type="object" icon="gtk-apply"/>
</tree>
</field>
</group>
</xpath>
</field>
</record>
</record>
</data>
</openerp>

View File

@ -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

View File

@ -23,5 +23,6 @@ import hr_department
import hr
import res_config
import res_users
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -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:

View File

@ -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):
@ -156,6 +158,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']
@ -164,10 +167,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),
@ -177,12 +180,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),
@ -213,25 +216,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):
@ -252,7 +272,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}
@ -265,17 +285,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
@ -291,6 +330,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"

View File

@ -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>

View File

@ -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="&quot;{
'domain': [
('to_read', '=', True),
('starred', '=', False),
],
'view_mailbox': True,
'view_inbox': True,
'read_action': 'read',
'show_compose_message': True
}&quot;"/>
</record>
</data>
</openerp>

65
addons/hr/res_users.py Normal file
View File

@ -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

View File

@ -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>

View File

@ -68,3 +68,8 @@
margin: 2px 0;
padding: 0;
}
.openerp .oe_employee_vignette .oe_followers {
width: auto;
float: none;
}

View File

@ -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'));
},
});
};

View File

@ -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&amp;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&amp;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>

View File

@ -46,7 +46,7 @@ class hr_attendance_error(osv.osv_memory):
cr.execute("SELECT id FROM hr_attendance WHERE employee_id IN %s AND to_char(name,'YYYY-mm-dd')<=%s AND to_char(name,'YYYY-mm-dd')>=%s AND action IN %s ORDER BY name" ,(tuple(context['active_ids']), date_to, date_from, tuple(['sign_in','sign_out'])))
attendance_ids = [x[0] for x in cr.fetchall()]
if not attendance_ids:
raise osv.except_osv(_('No Data Available !'), _('No records are found for your selection!'))
raise osv.except_osv(_('No Data Available!'), _('No records are found for your selection!'))
attendance_records = self.pool.get('hr.attendance').browse(cr, uid, attendance_ids, context=context)
for rec in attendance_records:

View File

@ -0,0 +1,232 @@
# Thai translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2013-06-15 17:09+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Thai <th@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-06-16 04:38+0000\n"
"X-Generator: Launchpad (build 16667)\n"
#. module: hr_contract
#: field:hr.contract,wage:0
msgid "Wage"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
msgid "Information"
msgstr "ข้อมูล"
#. module: hr_contract
#: field:hr.contract,trial_date_start:0
msgid "Trial Start Date"
msgstr "วันที่เริ่มต้นการทดลอง"
#. module: hr_contract
#: field:hr.employee,vehicle:0
msgid "Company Vehicle"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
msgid "Group By..."
msgstr "จัดกลุ่มตาม..."
#. module: hr_contract
#: field:hr.contract,department_id:0
msgid "Department"
msgstr "แผนก"
#. module: hr_contract
#: view:hr.contract:0
#: field:hr.contract,employee_id:0
#: model:ir.model,name:hr_contract.model_hr_employee
msgid "Employee"
msgstr "พนักงาน"
#. module: hr_contract
#: view:hr.contract:0
msgid "Search Contract"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
#: view:hr.employee:0
#: field:hr.employee,contract_ids:0
#: model:ir.actions.act_window,name:hr_contract.act_hr_employee_2_hr_contract
#: model:ir.actions.act_window,name:hr_contract.action_hr_contract
#: model:ir.ui.menu,name:hr_contract.hr_menu_contract
msgid "Contracts"
msgstr "สัญญา"
#. module: hr_contract
#: field:hr.employee,children:0
msgid "Number of Children"
msgstr "จำนวนบุตร"
#. module: hr_contract
#: help:hr.employee,contract_id:0
msgid "Latest contract of the employee"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
msgid "Job"
msgstr "งาน"
#. module: hr_contract
#: field:hr.contract,advantages:0
msgid "Advantages"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
msgid "Work Permit"
msgstr "ใบอนุญาตทำงาน"
#. module: hr_contract
#: model:ir.actions.act_window,name:hr_contract.action_hr_contract_type
#: model:ir.ui.menu,name:hr_contract.hr_menu_contract_type
msgid "Contract Types"
msgstr "ประเภทสัญญา"
#. module: hr_contract
#: view:hr.employee:0
msgid "Medical Exam"
msgstr ""
#. module: hr_contract
#: field:hr.contract,date_end:0
msgid "End Date"
msgstr "วันสิ้นสุด"
#. module: hr_contract
#: help:hr.contract,wage:0
msgid "Basic Salary of the employee"
msgstr "เงินเดือนของพนักงาน"
#. module: hr_contract
#: view:hr.contract:0
#: field:hr.contract,name:0
msgid "Contract Reference"
msgstr "อ้างอิงตามสัญญา"
#. module: hr_contract
#: help:hr.employee,vehicle_distance:0
msgid "In kilometers"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
#: field:hr.contract,notes:0
msgid "Notes"
msgstr ""
#. module: hr_contract
#: field:hr.contract,permit_no:0
msgid "Work Permit No"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
#: view:hr.employee:0
#: field:hr.employee,contract_id:0
#: model:ir.model,name:hr_contract.model_hr_contract
#: model:ir.ui.menu,name:hr_contract.next_id_56
msgid "Contract"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
#: field:hr.contract,type_id:0
#: view:hr.contract.type:0
#: field:hr.contract.type,name:0
#: model:ir.model,name:hr_contract.model_hr_contract_type
msgid "Contract Type"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
#: field:hr.contract,working_hours:0
msgid "Working Schedule"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
msgid "Salary and Advantages"
msgstr ""
#. module: hr_contract
#: field:hr.contract,job_id:0
msgid "Job Title"
msgstr ""
#. module: hr_contract
#: constraint:hr.contract:0
msgid "Error! Contract start-date must be less than contract end-date."
msgstr ""
#. module: hr_contract
#: field:hr.employee,manager:0
msgid "Is a Manager"
msgstr ""
#. module: hr_contract
#: field:hr.contract,date_start:0
msgid "Start Date"
msgstr ""
#. module: hr_contract
#: field:hr.contract,visa_no:0
msgid "Visa No"
msgstr ""
#. module: hr_contract
#: field:hr.employee,vehicle_distance:0
msgid "Home-Work Dist."
msgstr ""
#. module: hr_contract
#: field:hr.employee,place_of_birth:0
msgid "Place of Birth"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
msgid "Trial Period Duration"
msgstr ""
#. module: hr_contract
#: view:hr.contract:0
msgid "Duration"
msgstr ""
#. module: hr_contract
#: field:hr.contract,visa_expire:0
msgid "Visa Expire Date"
msgstr ""
#. module: hr_contract
#: field:hr.employee,medic_exam:0
msgid "Medical Examination Date"
msgstr ""
#. module: hr_contract
#: field:hr.contract,trial_date_end:0
msgid "Trial End Date"
msgstr ""
#. module: hr_contract
#: view:hr.contract.type:0
msgid "Search Contract Type"
msgstr ""

View File

@ -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','&lt;&gt;','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)]}"/>

View File

@ -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"/>

View File

@ -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>

View File

@ -49,7 +49,7 @@ class hr_payslip_employees(osv.osv_memory):
to_date = run_data.get('date_end', False)
credit_note = run_data.get('credit_note', False)
if not data['employee_ids']:
raise osv.except_osv(_("Warning !"), _("You must select employee(s) to generate payslip(s)."))
raise osv.except_osv(_("Warning!"), _("You must select employee(s) to generate payslip(s)."))
for emp in emp_pool.browse(cr, uid, data['employee_ids'], context=context):
slip_data = slip_pool.onchange_employee_id(cr, uid, [], from_date, to_date, emp.id, contract_id=False, context=context)
res = {

View File

@ -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>

View File

@ -181,7 +181,7 @@ class account_analytic_line(osv.osv):
for account in analytic_account_obj.browse(cr, uid, list(account_ids), context=context):
partner = account.partner_id
if (not partner) or not (account.pricelist_id):
raise osv.except_osv(_('Analytic Account incomplete !'),
raise osv.except_osv(_('Analytic Account Incomplete!'),
_('Contract incomplete. Please fill in the Customer and Pricelist fields.'))
date_due = False

View File

@ -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"/>

View File

@ -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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 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
4 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
5 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
6 access_hr_timesheet_report hr.timesheet.report model_hr_timesheet_report base.group_hr_manager 1 1 1 0 1 0
7 access_hr_analytic_timesheet_system_user hr.analytic.timesheet.system.user model_hr_analytic_timesheet base.group_user 1 0 0 0
8 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
9 access_timesheet_report timesheet.report model_timesheet_report base.group_hr_manager 1 1 1 0 1 0

View File

@ -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) {

View File

@ -84,7 +84,7 @@ class l10n_be_vat_declaration(osv.osv_memory):
obj_company = obj_user.browse(cr, uid, uid, context=context).company_id
vat_no = obj_company.partner_id.vat
if not vat_no:
raise osv.except_osv(_('insufficient data!'), _('No VAT number associated with your company.'))
raise osv.except_osv(_('Insufficient Data!'), _('No VAT number associated with your company.'))
vat_no = vat_no.replace(' ','').upper()
vat = vat_no[2:]

View File

@ -60,12 +60,12 @@ class partner_vat(osv.osv_memory):
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
period_ids = obj_period.search(cr, uid, [('date_start' ,'>=', date_start), ('date_stop','<=',date_stop), ('company_id','=',company_id)])
if not period_ids:
raise osv.except_osv(_('insufficient data!'), _('No data for the selected year.'))
raise osv.except_osv(_('Insufficient Data!'), _('No data for the selected year.'))
partners = []
partner_ids = obj_partner.search(cr, uid, [('vat_subjected', '!=', False), ('vat','ilike','BE%')], context=context)
if not partner_ids:
raise osv.except_osv(_('Error'),_('No belgian contact with a VAT number in your database.'))
raise osv.except_osv(_('Error'),_('No belgium contact with a VAT number in your database.'))
cr.execute("""SELECT sub1.partner_id, sub1.name, sub1.vat, sub1.turnover, sub2.vat_amount
FROM (SELECT l.partner_id, p.name, p.vat, SUM(CASE WHEN c.code ='49' THEN -l.tax_amount ELSE l.tax_amount END) as turnover
FROM account_move_line l
@ -90,7 +90,7 @@ class partner_vat(osv.osv_memory):
partners.append(id_client)
if not partners:
raise osv.except_osv(_('insufficient data!'), _('No data found for the selected year.'))
raise osv.except_osv(_('Insufficient Data!'), _('No data found for the selected year.'))
context.update({'partner_ids': partners, 'year': data['year'], 'limit_amount': data['limit_amount']})
model_data_ids = obj_model_data.search(cr, uid, [('model','=','ir.ui.view'), ('name','=','view_vat_listing')])
resource_id = obj_model_data.read(cr, uid, model_data_ids, fields=['res_id'])[0]['res_id']

View File

@ -108,7 +108,7 @@ class partner_vat_intra(osv.osv_memory):
# Get Company vat
company_vat = data_company.partner_id.vat
if not company_vat:
raise osv.except_osv(_('insufficient data!'),_('No VAT number associated with your company.'))
raise osv.except_osv(_('Insufficient Data!'),_('No VAT number associated with your company.'))
company_vat = company_vat.replace(' ','').upper()
issued_by = company_vat[:2]
@ -120,7 +120,7 @@ class partner_vat_intra(osv.osv_memory):
p_id_list = obj_partner.search(cr, uid, [('vat','!=',False)], context=context)
if not p_id_list:
raise osv.except_osv(_('Insufficient Data!'),_('No partner has a VAT number asociated with him.'))
raise osv.except_osv(_('Insufficient Data!'),_('No partner has a VAT number associated with him.'))
seq_declarantnum = obj_sequence.get(cr, uid, 'declarantnum')
dnum = company_vat[2:] + seq_declarantnum[-4:]

View File

@ -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>

View File

@ -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', '&lt;&gt;', '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', '&lt;&gt;', 'view')]"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('type', '&lt;&gt;', 'view')]"/>
<field name="amount"/>
<field name="note"/>
</tree>
</field>
</record>

View File

@ -103,7 +103,7 @@ class payroll_advice(osv.osv):
slip_ids = payslip_pool.search(cr, uid, [('date_from', '<=', advice.date), ('date_to', '>=', advice.date), ('state', '=', 'done')], context=context)
for slip in payslip_pool.browse(cr, uid, slip_ids, context=context):
if not slip.employee_id.bank_account_id and not slip.employee_id.bank_account_id.acc_number:
raise osv.except_osv(_('Error !'), _('Please define bank account for the %s employee') % (slip.employee_id.name))
raise osv.except_osv(_('Error!'), _('Please define bank account for the %s employee') % (slip.employee_id.name))
line_ids = payslip_line_pool.search(cr, uid, [ ('slip_id', '=', slip.id), ('code', '=', 'NET')], context=context)
if line_ids:
line = payslip_line_pool.browse(cr, uid, line_ids, context=context)[0]
@ -129,7 +129,7 @@ class payroll_advice(osv.osv):
seq_obj = self.pool.get('ir.sequence')
for advice in self.browse(cr, uid, ids, context=context):
if not advice.line_ids:
raise osv.except_osv(_('Error !'), _('You can not confirm Payment advice without advice lines.'))
raise osv.except_osv(_('Error!'), _('You can not confirm Payment advice without advice lines.'))
advice_date = datetime.strptime(advice.date, DATETIME_FORMAT)
advice_year = advice_date.strftime('%m') + '-' + advice_date.strftime('%Y')
number = seq_obj.get(cr, uid, 'payment.advice')
@ -183,7 +183,7 @@ class hr_payslip_run(osv.osv):
users = self.pool.get('res.users').browse(cr, uid, [uid], context=context)
for run in self.browse(cr, uid, ids, context=context):
if run.available_advice:
raise osv.except_osv(_('Error !'), _("Payment advice already exists for %s, 'Set to Draft' to create a new advice.") %(run.name))
raise osv.except_osv(_('Error!'), _("Payment advice already exists for %s, 'Set to Draft' to create a new advice.") %(run.name))
advice_data = {
'batch_id': run.id,
'company_id': users[0].company_id.id,
@ -201,7 +201,7 @@ class hr_payslip_run(osv.osv):
for slip in payslip_pool.browse(cr, uid, slip_ids, context=context):
if not slip.employee_id.bank_account_id or not slip.employee_id.bank_account_id.acc_number:
raise osv.except_osv(_('Error !'), _('Please define bank account for the %s employee') % (slip.employee_id.name))
raise osv.except_osv(_('Error!'), _('Please define bank account for the %s employee') % (slip.employee_id.name))
line_ids = payslip_line_pool.search(cr, uid, [('slip_id', '=', slip.id), ('code', '=', 'NET')], context=context)
if line_ids:
line = payslip_line_pool.browse(cr, uid, line_ids, context=context)[0]

File diff suppressed because it is too large Load Diff

View File

@ -64,7 +64,7 @@ class report_custom(report_int):
self.obj.render()
return (self.obj.pdf, 'pdf')
except Exception:
raise osv.except_osv(_('pdf not created !'), _('Please check if package pdftk is installed!'))
raise osv.except_osv(_('PDF Not Created!'), _('Please check if package pdftk is installed!'))
report_custom('report.l10n_lu.tax.report.print')

View File

@ -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:

View File

@ -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

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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"/>

View File

@ -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]

View File

@ -17,7 +17,8 @@
],
'view_mailbox': True,
'view_inbox': True,
'read_action': 'read'
'read_action': 'read',
'show_compose_message': False
}&quot;"/>
<field name="help" type="html">
<p>

View File

@ -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:

View File

@ -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):

View File

@ -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>

View File

@ -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;
}

View File

@ -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;

View File

@ -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)
};

View File

@ -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'));
},
});
};

Some files were not shown because too many files have changed in this diff Show More