[MERGE] from trunk

bzr revid: rco@openerp.com-20121001073103-q8fajwquos2rm57f
This commit is contained in:
Raphael Collet 2012-10-01 09:31:03 +02:00
commit 27b4a70343
151 changed files with 1218 additions and 721 deletions

View File

@ -31,7 +31,7 @@ import decimal_precision as dp
from tools.translate import _
from tools.float_utils import float_round
from openerp import SUPERUSER_ID
import tools
_logger = logging.getLogger(__name__)
@ -227,7 +227,7 @@ class account_account(osv.osv):
while pos < len(args):
if args[pos][0] == 'code' and args[pos][1] in ('like', 'ilike') and args[pos][2]:
args[pos] = ('code', '=like', str(args[pos][2].replace('%', ''))+'%')
args[pos] = ('code', '=like', tools.ustr(args[pos][2].replace('%', ''))+'%')
if args[pos][0] == 'journal_id':
if not args[pos][2]:
del args[pos]
@ -682,7 +682,7 @@ class account_journal_view(osv.osv):
_name = "account.journal.view"
_description = "Journal View"
_columns = {
'name': fields.char('Journal View', size=64, required=True),
'name': fields.char('Journal View', size=64, required=True, translate=True),
'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
}
_order = "name"
@ -1908,7 +1908,7 @@ class account_tax(osv.osv):
'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
'include_base_amount': fields.boolean('Included in base amount', help="Indicates if the amount of tax must be included in the base amount for the computation of the next taxes"),
'company_id': fields.many2one('res.company', 'Company', required=True),
'description': fields.char('Tax Code',size=32),
'description': fields.char('Tax Code'),
'price_include': fields.boolean('Tax Included in Price', help="Check this if the price you use on the product and invoices includes this tax."),
'type_tax_use': fields.selection([('sale','Sale'),('purchase','Purchase'),('all','All')], 'Tax Application', required=True)
@ -2518,7 +2518,7 @@ class account_account_template(osv.osv):
'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if you want the user to reconcile entries in this account."),
'shortcut': fields.char('Shortcut', size=12),
'note': fields.text('Note'),
'parent_id': fields.many2one('account.account.template', 'Parent Account Template', ondelete='cascade'),
'parent_id': fields.many2one('account.account.template', 'Parent Account Template', ondelete='cascade', domain=[('type','=','view')]),
'child_parent_ids':fields.one2many('account.account.template', 'parent_id', 'Children'),
'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel', 'account_id', 'tax_id', 'Default Taxes'),
'nocreate': fields.boolean('Optional create', help="If checked, the new chart of accounts will not contain this by default."),
@ -2536,20 +2536,6 @@ class account_account_template(osv.osv):
(_check_recursion, 'Error!\nYou cannot create recursive account templates.', ['parent_id']),
]
def create(self, cr, uid, vals, context=None):
if 'parent_id' in vals:
parent = self.read(cr, uid, [vals['parent_id']], ['type'])
if parent and parent[0]['type'] != 'view':
raise osv.except_osv(_('Warning!'), _("You may only select a parent account of type 'View'."))
return super(account_account_template, self).create(cr, uid, vals, context=context)
def write(self, cr, uid, ids, vals, context=None):
if 'parent_id' in vals:
parent = self.read(cr, uid, [vals['parent_id']], ['type'])
if parent and parent[0]['type'] != 'view':
raise osv.except_osv(_('Warning!'), _("You may only select a parent account of type 'View'."))
return super(account_account_template, self).write(cr, uid, ids, vals, context=context)
def name_get(self, cr, uid, ids, context=None):
if not ids:
return []
@ -2828,7 +2814,7 @@ class account_tax_template(osv.osv):
'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
'include_base_amount': fields.boolean('Include in Base Amount', help="Set if the amount of tax must be included in the base amount before computing the next taxes."),
'description': fields.char('Internal Name', size=32),
'description': fields.char('Internal Name'),
'type_tax_use': fields.selection([('sale','Sale'),('purchase','Purchase'),('all','All')], 'Tax Use In', required=True,),
'price_include': fields.boolean('Tax Included in Price', help="Check this if the price you use on the product and invoices includes this tax."),
}

View File

@ -457,6 +457,8 @@ class account_bank_statement(osv.osv):
return res and res[0] or 0.0
def onchange_journal_id(self, cr, uid, statement_id, journal_id, context=None):
if not journal_id:
return {}
balance_start = self._compute_balance_end_real(cr, uid, journal_id, context=context)
journal_data = self.pool.get('account.journal').read(cr, uid, journal_id, ['default_debit_account_id', 'company_id'], context=context)

View File

@ -508,8 +508,10 @@ class account_invoice(osv.osv):
if journal_id:
journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
currency_id = journal.currency and journal.currency.id or journal.company_id.currency_id.id
company_id = journal.company_id.id
result = {'value': {
'currency_id': currency_id,
'company_id': company_id,
}
}
return result
@ -1371,7 +1373,10 @@ class account_invoice_line(osv.osv):
'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True)
}
def _default_account_id(self, cr, uid, ids, context=None):
def _default_account_id(self, cr, uid, context=None):
# XXX this gets the default account for the user's company,
# it should get the default account for the invoice's company
# however, the invoice's company does not reach this point
prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context)
return prop and prop.id or False
@ -1401,7 +1406,7 @@ class account_invoice_line(osv.osv):
context = {}
company_id = company_id if company_id != None else context.get('company_id',False)
context = dict(context)
context.update({'company_id': company_id})
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 !") )
if not product:
@ -1556,16 +1561,19 @@ class account_invoice_line(osv.osv):
def onchange_account_id(self, cr, uid, ids, product_id, partner_id, inv_type, fposition_id, account_id):
if not account_id:
return {}
taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids
unique_tax_ids = []
fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
tax_ids = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
product_change_result = self.product_id_change(cr, uid, ids, product_id, False, type=inv_type,
partner_id=partner_id, fposition_id=fposition_id)
unique_tax_ids = set(tax_ids)
if product_change_result and 'value' in product_change_result and 'invoice_line_tax_id' in product_change_result['value']:
unique_tax_ids |= set(product_change_result['value']['invoice_line_tax_id'])
return {'value':{'invoice_line_tax_id': list(unique_tax_ids)}}
account = self.pool.get('account.account').browse(cr, uid, account_id)
if not product_id:
taxes = account.tax_ids
unique_tax_ids = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
else:
product_change_result = self.product_id_change(cr, uid, ids, product_id, False, type=inv_type,
partner_id=partner_id, fposition_id=fposition_id,
company_id=account.company_id.id)
if product_change_result and 'value' in product_change_result and 'invoice_line_tax_id' in product_change_result['value']:
unique_tax_ids = product_change_result['value']['invoice_line_tax_id']
return {'value':{'invoice_line_tax_id': unique_tax_ids}}
account_invoice_line()
@ -1650,14 +1658,13 @@ class account_invoice_tax(osv.osv):
for line in inv.invoice_line:
for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, line.product_id, inv.partner_id)['taxes']:
tax['price_unit'] = cur_obj.round(cr, uid, cur, tax['price_unit'])
val={}
val['invoice_id'] = inv.id
val['name'] = tax['name']
val['amount'] = tax['amount']
val['manual'] = False
val['sequence'] = tax['sequence']
val['base'] = tax['price_unit'] * line['quantity']
val['base'] = cur_obj.round(cr, uid, cur, tax['price_unit'] * line['quantity'])
if inv.type in ('out_invoice','in_invoice'):
val['base_code_id'] = tax['base_code_id']

View File

@ -278,6 +278,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@ -299,8 +300,8 @@
<button name="invoice_open" states="draft" string="Validate" class="oe_highlight"/>
<button name="invoice_open" states="proforma2" string="Validate"/>
<button name="invoice_proforma2" states="draft" string="PRO-FORMA" groups="account.group_proforma_invoices"/>
<button name="%(action_account_invoice_refund)d" type='action' string='Refund Invoice' states='paid'/>
<button name="invoice_cancel" states="draft,proforma2,sale,open" string="Cancel" groups="base.group_no_one"/>
<button name="%(action_account_invoice_refund)d" type='action' string='Refund Invoice' states='open,proforma2,paid'/>
<button name="invoice_cancel" states="draft,proforma2,open" string="Cancel" groups="base.group_no_one"/>
<button name="action_cancel_draft" states="cancel" string="Reset to Draft" type="object"/>
<button name='%(action_account_state_open)d' type='action' string='Re-Open' groups="account.group_account_invoice" attrs="{'invisible':['|', ('state','&lt;&gt;','paid'), ('reconciled', '=', True)]}" help="This button only appears when the state of the invoice is 'paid' (showing that it has been fully reconciled) and auto-computed boolean 'reconciled' is False (depicting that it's not the case anymore). In other words, the invoice has been dereconciled and it does not fit anymore the 'paid' state. You should press this button to re-open it and let it continue its normal process after having resolved the eventual exceptions it may have created."/>
<!--button name="%(account_invoices)d" string="Print Invoice" type="action" states="open,paid,proforma,sale,proforma2"/-->
@ -437,6 +438,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -562,6 +562,7 @@ class account_move_line(osv.osv):
'journal_id': lambda self, cr, uid, c: c.get('journal_id', False),
'credit': 0.0,
'debit': 0.0,
'amount_currency': 0.0,
'account_id': lambda self, cr, uid, c: c.get('account_id', False),
'period_id': lambda self, cr, uid, c: c.get('period_id', False),
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.move.line', context=c)
@ -1193,12 +1194,12 @@ class account_move_line(osv.osv):
jour_period_obj = self.pool.get('account.journal.period')
cr.execute('SELECT state FROM account_journal_period WHERE journal_id = %s AND period_id = %s', (journal_id, period_id))
result = cr.fetchall()
journal = journal_obj.browse(cr, uid, journal_id, context=context)
period = period_obj.browse(cr, uid, period_id, context=context)
for (state,) in result:
if state == 'done':
raise osv.except_osv(_('Error!'), _('You cannot add/modify entries in a closed journal.'))
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:
journal = journal_obj.browse(cr, uid, journal_id, context=context)
period = period_obj.browse(cr, uid, period_id, context=context)
jour_period_obj.create(cr, uid, {
'name': (journal.code or journal.name)+':'+(period.name or ''),
'journal_id': journal.id,

View File

@ -1305,16 +1305,6 @@
Account.Entry Edition
-->
<record id="account_move_graph" model="ir.ui.view">
<field name="name">account.move.graph</field>
<field name="model">account.move</field>
<field name="arch" type="xml">
<graph string="Account Statistics" type="bar">
<field name="period_id"/>
<field name="amount" operator="+"/>
</graph>
</field>
</record>
<record id="view_move_tree" model="ir.ui.view">
<field name="name">account.move.tree</field>
<field name="model">account.move</field>
@ -1338,8 +1328,8 @@
<field name="arch" type="xml">
<form string="Account Entry" version="7.0">
<header>
<button name="button_validate" states="draft" string="Post" type="object" class="oe_highlight"/>
<button name="button_cancel" states="posted" string="Cancel" type="object"/>
<button name="button_validate" states="draft" string="Post" type="object" class="oe_highlight" groups="account.group_account_invoice"/>
<button name="button_cancel" states="posted" string="Cancel" type="object" groups="account.group_account_invoice"/>
<field name="state" widget="statusbar"/>
</header>
<sheet string="Journal Entries" >
@ -1488,7 +1478,7 @@
<field name="name">Journal Entries</field>
<field name="res_model">account.move</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_move_tree"/>
<field name="search_view_id" ref="view_account_move_filter"/>
<field name="help" type="html">
@ -2037,7 +2027,7 @@
src_model="account.journal"/>
<act_window
context="{'search_default_reconcile_id':False, 'search_default_partner_id':[active_id], 'default_partner_id': active_id}"
context="{'search_default_unreconciled':True, 'search_default_partner_id':[active_id], 'default_partner_id': active_id}"
domain="[('account_id.reconcile', '=', True),('account_id.type', 'in', ['receivable', 'payable'])]"
id="act_account_partner_account_move_all"
name="Receivables &amp; Payables"
@ -2073,7 +2063,7 @@
id="account_template_folder"
name="Templates"
parent="menu_finance_accounting"
groups="base.group_multi_company"/>
groups="account.group_account_manager"/>
<menuitem
id="account_template_taxes"
name="Taxes"

View File

@ -44,17 +44,17 @@ class account_fiscal_position(osv.osv):
return []
if not fposition_id:
return map(lambda x: x.id, taxes)
result = []
result = set()
for t in taxes:
ok = False
for tax in fposition_id.tax_ids:
if tax.tax_src_id.id == t.id:
if tax.tax_dest_id:
result.append(tax.tax_dest_id.id)
result.add(tax.tax_dest_id.id)
ok=True
if not ok:
result.append(t.id)
return result
result.add(t.id)
return list(result)
def map_account(self, cr, uid, fposition_id, account_id, context=None):
if not fposition_id:
@ -77,6 +77,12 @@ class account_fiscal_position_tax(osv.osv):
'tax_dest_id': fields.many2one('account.tax', 'Replacement Tax')
}
_sql_constraints = [
('tax_src_dest_uniq',
'unique (position_id,tax_src_id,tax_dest_id)',
'A tax fiscal position could be defined only once time on same taxes.')
]
account_fiscal_position_tax()
class account_fiscal_position_account(osv.osv):
@ -89,6 +95,12 @@ class account_fiscal_position_account(osv.osv):
'account_dest_id': fields.many2one('account.account', 'Account Destination', domain=[('type','<>','view')], required=True)
}
_sql_constraints = [
('account_src_dest_uniq',
'unique (position_id,account_src_id,account_dest_id)',
'An account fiscal position could be defined only once time on same accounts.')
]
account_fiscal_position_account()
class res_partner(osv.osv):

View File

@ -92,34 +92,7 @@
<field name="debit"/>
</group>
</group>
<field name="bank_ids">
<form string="Bank account" version="7.0">
<group col="4">
<field name="state"/>
<field name="acc_number"/>
</group>
<group>
<group name="owner" string="Bank Account Owner">
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
<field name="owner_name"/>
<label for="street" string="Address"/>
<div>
<field name="street" placeholder="Street..."/>
<div>
<field name="zip" class="oe_inline" placeholder="ZIP"/>
<field name="city" class="oe_inline" placeholder="City"/>
</div>
<field name="state_id" placeholder="State" options='{"no_open": true}'/>
<field name="country_id" placeholder="Country" options='{"no_open": true}'/>
</div>
</group>
<group name="bank" string="Information About the Bank">
<field name="bank" on_change="onchange_bank_id(bank)"/>
<field name="bank_name"/>
<field name="bank_bic" placeholder="e.g. GEBABEBB"/>
</group>
</group>
</form>
<field name="bank_ids" context="{'default_partner_id': active_id, 'form_view_ref': 'base.view_partner_bank_form'}">
<tree string="Bank Details">
<field name="sequence" invisible="1"/>
<field name="acc_number"/>
@ -141,7 +114,7 @@
context="{'search_default_partner_id':[active_id], 'default_partner_id': active_id}"
src_model="res.partner"
view_type="form"
view_mode="tree,form,graph,calendar"/>
view_mode="tree,form,calendar"/>
</data>
</openerp>

View File

@ -73,7 +73,7 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph,form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_account_analytic_account_tree"/>
<field name="search_view_id" ref="account.view_account_analytic_account_search"/>
</record>
@ -114,9 +114,6 @@
</field>
</record>
<menuitem groups="analytic.group_analytic_accounting" id="next_id_40"
name="Analytic" parent="account.menu_finance_generic_reporting"
sequence="4"/>
<record id="view_account_analytic_line_form" model="ir.ui.view">
<field name="name">account.analytic.line.form</field>
@ -351,15 +348,6 @@
# Reporting
#
<record id="action_account_analytic_journal_tree" model="ir.actions.act_window">
<field name="name">Print Analytic Journals</field>
<field name="res_model">account.analytic.journal</field>
<field name="view_type">tree</field>
<field name="help">To print an analytics (or costs) journal for a given period. The report give code, move name, account number, general amount and analytic amount.</field>
</record>
<menuitem groups="analytic.group_analytic_accounting"
action="action_account_analytic_journal_tree"
id="account_analytic_journal_print" parent="account.next_id_40"/>
<record id="view_account_journal_1" model="ir.ui.view">
<field name="name">account.journal.form.1</field>
@ -372,16 +360,5 @@
</field>
</record>
<record id="analytic_accounts_graph" model="ir.ui.view">
<field name="name">analytic.accounts.graph</field>
<field name="model">account.analytic.account</field>
<field name="arch" type="xml">
<graph string="Analytic Account Statistics" type="bar">
<field name="complete_name"/>
<field name="balance" operator="+"/>
</graph>
</field>
</record>
</data>
</openerp>

View File

@ -29,6 +29,7 @@ class account_analytic_journal_report(osv.osv_memory):
_columns = {
'date1': fields.date('Start of period', required=True),
'date2': fields.date('End of period', required=True),
'analytic_account_journal_id': fields.many2many('account.analytic.journal', 'account_analytic_journal_name', 'journal_line_id', 'journal_print_id', 'Analytic Journals', required=True),
}
_defaults = {
@ -40,8 +41,15 @@ class account_analytic_journal_report(osv.osv_memory):
if context is None:
context = {}
data = self.read(cr, uid, ids)[0]
ids_list = []
if context.get('active_id',False):
ids_list.append(context.get('active_id',False))
else:
record = self.browse(cr,uid,ids[0],context=context)
for analytic_record in record.analytic_account_journal_id:
ids_list.append(analytic_record.id)
datas = {
'ids': context.get('active_ids',[]),
'ids': ids_list,
'model': 'account.analytic.journal',
'form': data
}
@ -50,6 +58,14 @@ class account_analytic_journal_report(osv.osv_memory):
'report_name': 'account.analytic.journal',
'datas': datas,
}
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
res = super(account_analytic_journal_report, self).default_get(cr, uid, fields, context=context)
if 'analytic_account_journal_id' in fields:
res.update({'analytic_account_journal_id': context.get('active_ids',[])})
return res
account_analytic_journal_report()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -7,15 +7,16 @@
<field name="model">account.analytic.journal.report</field>
<field name="arch" type="xml">
<form string="Select Period" version="7.0">
<header>
<button name="check_report" string="Print" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel"/>
</header>
<group col="4">
<field name="date1"/>
<field name="date2"/>
<field name="analytic_account_journal_id" widget="many2many_tags" class="oe_inline" required="1"/>
</group>
<footer>
<button name="check_report" string="Print" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
@ -38,6 +39,13 @@
<field name="key">action</field>
<field name="model">account.analytic.journal</field>
</record>
<menuitem groups="analytic.group_analytic_accounting" id="next_id_40"
name="Analytic" parent="account.menu_finance_generic_reporting"
sequence="4"/>
<menuitem groups="analytic.group_analytic_accounting"
action="account.action_account_analytic_journal"
id="account_analytic_journal_print" parent="account.next_id_40"/>
</data>
</openerp>

View File

@ -32,11 +32,11 @@ class account_invoice_refund(osv.osv_memory):
_name = "account.invoice.refund"
_description = "Invoice Refund"
_columns = {
'date': fields.date('Operation Date', help='This date will be used as the invoice date for credit note and period will be chosen accordingly!'),
'date': fields.date('Date', help='This date will be used as the invoice date for credit note and period will be chosen accordingly!'),
'period': fields.many2one('account.period', 'Force period'),
'journal_id': fields.many2one('account.journal', 'Refund Journal', help='You can select here the journal to use for the credit note that will be created. If you leave that field empty, it will use the same journal as the current invoice.'),
'description': fields.char('Description', size=128, required=True),
'filter_refund': fields.selection([('refund', 'Create a draft refund'), ('cancel', 'Cancel: create credit note and reconcile'),('modify', 'Modify: create credit note, reconcile and create a new draft invoice')], "Refund Method", required=True, help='Credit note base on this type. You can not Modify and Cancel if the invoice is already reconciled'),
'description': fields.char('Reason', size=128, required=True),
'filter_refund': fields.selection([('refund', 'Create a draft credit note'), ('cancel', 'Cancel: create credit note and reconcile'),('modify', 'Modify: create credit note, reconcile and create a new draft invoice')], "Refund Method", required=True, help='Credit note base on this type. You can not Modify and Cancel if the invoice is already reconciled'),
}
def _get_journal(self, cr, uid, context=None):

View File

@ -7,19 +7,40 @@
<field name="model">account.invoice.refund</field>
<field name="arch" type="xml">
<form string="Credit Note" version="7.0">
<separator string="Credit Note Options"/>
<group col="4">
<field name="description"/>
<field name="journal_id" widget='selection'/>
<field name="date"/>
<field name="period"/>
<field name="filter_refund"/>
<group>
<group colspan="2">
<label for="filter_refund"/>
<div>
<field name="filter_refund" class="oe_inline"/>
<p attrs="{'invisible':[('filter_refund','&lt;&gt;','refund')]}" class="oe_grey">
You will be able to edit and validate this
credit note directly or keep it draft,
waiting for the document to be issued by
your supplier/customer.
</p>
<p attrs="{'invisible':[('filter_refund','&lt;&gt;','cancel')]}" class="oe_grey">
Use this option if you want to cancel an invoice you should not
have issued. The credit note will be created, validated and reconciled
with the invoice. You will not be able to modify the credit note.
</p>
<p attrs="{'invisible':[('filter_refund','&lt;&gt;','modify')]}" class="oe_grey">
Use this option if you want to cancel an invoice and create a new
one. The credit note will be created, validated and reconciled
with the current invoice. A new, draft, invoice will be created
so that you can edit it.
</p>
</div>
</group>
<group>
<field name="description"/>
<field name="journal_id" widget='selection'/>
</group><group>
<field name="date"/>
<field name="period"/>
</group>
</group>
<label string="Modify Invoice: Cancels the current invoice and creates a new copy of it ready for editing."/>
<label string="Credit Note: Creates the credit note, ready for editing."/>
<label string="Cancel Invoice: Creates the credit note, validate and reconcile it to cancel the current invoice."/>
<footer>
<button string='Refund' name="invoice_refund" type="object" class="oe_highlight"/>
<button string='Create Credit Note' name="invoice_refund" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
@ -28,7 +49,7 @@
</record>
<record id="action_account_invoice_refund" model="ir.actions.act_window">
<field name="name">Refund</field>
<field name="name">Refund Invoice</field>
<field name="res_model">account.invoice.refund</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>

View File

@ -23,6 +23,7 @@ import time
from osv import fields, osv
from tools.translate import _
import decimal_precision as dp
class account_move_line_reconcile(osv.osv_memory):
"""
@ -32,9 +33,9 @@ class account_move_line_reconcile(osv.osv_memory):
_description = 'Account move line reconcile'
_columns = {
'trans_nbr': fields.integer('# of Transaction', readonly=True),
'credit': fields.float('Credit amount', readonly=True),
'debit': fields.float('Debit amount', readonly=True),
'writeoff': fields.float('Write-Off amount', readonly=True),
'credit': fields.float('Credit amount', readonly=True, digits_compute=dp.get_precision('Account')),
'debit': fields.float('Debit amount', readonly=True, digits_compute=dp.get_precision('Account')),
'writeoff': fields.float('Write-Off amount', readonly=True, digits_compute=dp.get_precision('Account')),
}
def default_get(self, cr, uid, fields, context=None):

View File

@ -48,7 +48,7 @@
<field name="name">Contracts to Renew</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="view_mode">tree,form</field>
<field name="context">{'search_default_user_id':uid, 'search_default_draft':1, 'search_default_pending':1, 'search_default_open':1, 'search_default_renew':1}</field>
<field name="domain">[('type','=','contract')]</field>
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
@ -72,7 +72,7 @@
<field name="name">Contracts</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="view_mode">tree,form</field>
<field name="context">{'default_type':'contract', 'search_default_open':1, 'search_default_pending':1}</field>
<field name="domain">[('type','=','contract')]</field>
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>

View File

@ -350,7 +350,7 @@ class account_asset_depreciation_line(osv.osv):
'amount': fields.float('Depreciation Amount', required=True),
'remaining_value': fields.float('Amount to Depreciate', required=True),
'depreciated_value': fields.float('Amount Already Depreciated', required=True),
'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
'depreciation_date': fields.date('Depreciation Date', select=1),
'move_id': fields.many2one('account.move', 'Depreciation Entry'),
'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Posted', store=True)
}
@ -368,7 +368,7 @@ class account_asset_depreciation_line(osv.osv):
for line in self.browse(cr, uid, ids, context=context):
if currency_obj.is_zero(cr, uid, line.asset_id.currency_id, line.remaining_value):
can_close = True
depreciation_date = line.asset_id.prorata and line.asset_id.purchase_date or time.strftime('%Y-%m-%d')
depreciation_date = time.strftime('%Y-%m-%d')
period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
company_currency = line.asset_id.company_id.currency_id.id
current_currency = line.asset_id.currency_id.id

View File

@ -57,6 +57,7 @@ class account_invoice_line(osv.osv):
'partner_id': line.invoice_id.partner_id.id,
'company_id': line.invoice_id.company_id.id,
'currency_id': line.invoice_id.currency_id.id,
'purchase_date' : line.invoice_id.date_invoice,
}
changed_vals = asset_obj.onchange_category_id(cr, uid, [], vals['category_id'], context=context)
vals.update(changed_vals['value'])

View File

@ -50,7 +50,7 @@ class asset_asset_report(osv.osv):
select
min(dl.id) as id,
dl.name as name,
to_date(dl.depreciation_date, 'YYYY-MM-DD') as depreciation_date,
dl.depreciation_date as depreciation_date,
a.purchase_date as purchase_date,
(CASE WHEN (select min(d.id) from account_asset_depreciation_line as d
left join account_asset_asset as ac ON (ac.id=d.asset_id)
@ -77,7 +77,7 @@ class asset_asset_report(osv.osv):
from account_asset_depreciation_line dl
left join account_asset_asset a on (dl.asset_id=a.id)
group by
dl.amount,dl.asset_id,to_date(dl.depreciation_date, 'YYYY-MM-DD'),dl.name,
dl.amount,dl.asset_id,dl.depreciation_date,dl.name,
a.purchase_date, dl.move_check, a.state, a.category_id, a.partner_id, a.company_id,
a.purchase_value, a.id, a.salvage_value
)""")

View File

@ -76,7 +76,7 @@
<field name="ref" readonly="1"/>
<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" domain="[('journal_id','=',parent.journal_id)]"/>
<field name="account_id" domain="[('journal_id','=',journal_id)]"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('company_id', '=', parent.company_id), ('type', '&lt;&gt;', 'view')]"/>
<field name="amount" readonly="1" sum="Total Amount"/>
<field name="globalisation_id" string="Glob. Id"/>
@ -100,7 +100,7 @@
<field name="ref" readonly="0"/>
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
<field name="type" on_change="onchange_type(partner_id, type)"/>
<field domain="[('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view')]" name="account_id"/>
<field domain="[('journal_id', '=', journal_id), ('type', '&lt;&gt;', 'view')]" name="account_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="globalisation_id"/>

View File

@ -88,7 +88,7 @@ class payment_order_create(osv.osv_memory):
'partner_id': line.partner_id and line.partner_id.id or False,
'communication': line.ref or '/',
'date': date_to_pay,
'currency': line.invoice and line.invoice.currency_id.id or False,
'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)
return {'type': 'ir.actions.act_window_close'}
@ -120,4 +120,4 @@ class payment_order_create(osv.osv_memory):
payment_order_create()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -32,11 +32,11 @@ class res_company(osv.osv):
_columns = {
'income_currency_exchange_account_id': fields.many2one(
'account.account',
string="Income Currency Rate",
string="Gain Exchange Rate Account",
domain="[('type', '=', 'other')]",),
'expense_currency_exchange_account_id': fields.many2one(
'account.account',
string="Expense Currency Rate",
string="Loss Exchange Rate Account",
domain="[('type', '=', 'other')]",),
}
@ -957,11 +957,11 @@ class account_voucher(osv.osv):
if amount_residual > 0:
account_id = line.voucher_id.company_id.expense_currency_exchange_account_id
if not account_id:
raise osv.except_osv(_('Warning!'),_("First you have to configure the 'Income Currency Rate' on the company, then create accounting entry for currency rate difference."))
raise osv.except_osv(_('Warning!'),_("First you have to configure the 'Expense Currency Rate' on the company, then create accounting entry for currency rate difference."))
else:
account_id = line.voucher_id.company_id.income_currency_exchange_account_id
if not account_id:
raise osv.except_osv(_('Warning!'),_("First you have to configure the 'Expense Currency Rate' on the company, then create accounting entry for currency rate difference."))
raise osv.except_osv(_('Warning!'),_("First you have to configure the 'Income Currency Rate' on the company, then create accounting entry for currency rate difference."))
# Even if the amount_currency is never filled, we need to pass the foreign currency because otherwise
# the receivable/payable account may have a secondary currency, which render this field mandatory
account_currency_id = company_currency <> current_currency and current_currency or False

View File

@ -108,6 +108,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -243,6 +243,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@ -518,6 +519,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -146,6 +146,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@ -301,6 +302,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -53,6 +53,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -442,11 +442,13 @@ class audittrail_objects_proxy(object_proxy):
# if at least one modification has been found
for model_id, resource_id in lines:
name = pool.get(model.model).name_get(cr, uid, [resource_id])[0][1]
vals = {
'method': method,
'object_id': model_id,
'user_id': uid,
'res_id': resource_id,
'name': name,
}
if (model_id, resource_id) not in old_values and method not in ('copy', 'read'):
# the resource was not existing so we are forcing the method to 'create'
@ -484,7 +486,7 @@ class audittrail_objects_proxy(object_proxy):
if len(rule['user_id']) == 0 or uid in rule['user_id']:
if rule.get('log_'+method,0):
return True
elif method not in ('default_get','read','fields_view_get','fields_get','search','search_count','name_search','name_get','get','request_get', 'get_sc', 'unlink', 'write', 'create'):
elif method not in ('default_get','read','fields_view_get','fields_get','search','search_count','name_search','name_get','get','request_get', 'get_sc', 'unlink', 'write', 'create', 'read_group', 'import_data'):
if rule['log_action']:
return True

View File

@ -180,8 +180,8 @@ class CompanyLDAP(osv.osv):
"""
user_id = False
login = tools.ustr(login)
cr.execute("SELECT id, active FROM res_users WHERE login=%s", (login,))
login = tools.ustr(login.lower())
cr.execute("SELECT id, active FROM res_users WHERE lower(login)=%s", (login,))
res = cr.fetchone()
if res:
if res[1]:

View File

@ -14,7 +14,7 @@
<field name="ldap_server"/>
<field name="ldap_server_port"/>
<field name="ldap_binddn"/>
<field name="ldap_password"/>
<field name="ldap_password" password="True"/>
<field name="ldap_base"/>
<field name="ldap_filter"/>
<field name="create_user"/>

View File

@ -971,8 +971,8 @@ class calendar_event(osv.osv):
event = datas['id']
if datas.get('interval', 0) < 0:
raise osv.except_osv(_('Warning!'), _('Interval cannot be negative.'))
if datas.get('count', 0) < 0:
raise osv.except_osv(_('Warning!'), _('Count cannot be negative.'))
if datas.get('count', 0) <= 0:
raise osv.except_osv(_('Warning!'), _('Count cannot be negative or 0.'))
if datas['recurrency']:
result[event] = self.compute_rule_string(datas)
else:
@ -1113,6 +1113,16 @@ rule or repeating pattern of time to exclude from the recurring rule."),
'user_id': lambda self, cr, uid, ctx: uid,
'organizer': default_organizer,
}
def _check_closing_date(self, cr, uid, ids, context=None):
for event in self.browse(cr, uid, ids, context=context):
if event.date_deadline < event.date:
return False
return True
_constraints = [
(_check_closing_date, 'Error ! End date cannot be set before start date.', ['date_deadline']),
]
def get_recurrent_ids(self, cr, uid, select, domain, limit=100, context=None):
"""Gives virtual event ids for recurring events based on value of Recurrence Rule

View File

@ -182,13 +182,14 @@
<button name="do_tentative"
states="needs-action,declined,accepted"
string="Uncertain" type="object"
icon="terp-crm"
/>
<button name="do_accept" string="Accept"
states="needs-action,tentative,declined"
type="object" />
type="object" icon="gtk-apply"/>
<button name="do_decline" string="Decline"
states="needs-action,tentative,accepted"
type="object" />
type="object" icon="gtk-cancel"/>
</tree>
<form string="Invitation details" version="7.0">
<header>
@ -222,6 +223,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -14,12 +14,13 @@
<p class="oe_view_nocontent_create">
Click to create an unqualified lead.
</p><p>
A lead is usually the first step in your sales cycle. It can be
a contact with a new prospect, a company you target, a contact form
filled in your website, etc.
Use leads if you need a qualification step before creating an
opportunity or a customer. It can be a business card you received,
a contact form filled in your website, or a file of unqualified
prospects you import, etc.
</p><p>
Once qualified, the lead can be converted into a business
opportunity and a new customer in your address book.
opportunity and/or a new customer in your address book.
</p>
</field>
</record>

View File

@ -223,6 +223,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@ -525,6 +526,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -6,8 +6,8 @@
id="crm_case_categ_phone_create_partner"
name="Schedule a Call"
res_model="crm.phonecall"
view_mode="calendar,tree,form"
context="{'search_default_partner_id': [active_id], 'default_duration': 1.0, 'default_partner_id': active_id}"
view_mode="tree,form,calendar"
context="{'search_default_partner_id': active_id, 'default_duration': 1.0, 'default_partner_id': active_id}"
groups="base.group_sale_salesman"
/>

View File

@ -146,10 +146,10 @@
<field name="priority"/>
<field name="opportunity_id"/>
</group>
<field name="description" placeholder="Description..."/>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -26,12 +26,16 @@ class res_partner(osv.osv):
_inherit = 'res.partner'
def _opportunity_meeting_count(self, cr, uid, ids, field_name, arg, context=None):
res = {}
for partner in self.browse(cr, uid, ids, context):
res[partner.id] = {
'opportunity_count': len(partner.opportunity_ids),
'meeting_count': len(partner.meeting_ids),
}
res = dict(map(lambda x: (x,{'opportunity_count': 0, 'meeting_count': 0}), ids))
# the user may not have access rights for opportunities or meetings
try:
for partner in self.browse(cr, uid, ids, context):
res[partner.id] = {
'opportunity_count': len(partner.opportunity_ids),
'meeting_count': len(partner.meeting_ids),
}
except:
pass
return res
_columns = {

View File

@ -54,7 +54,7 @@
<field name="src_model">res.partner</field>
<field name="res_model">crm.meeting</field>
<field name="view_mode">calendar,tree,form,gantt</field>
<field name="context">{'search_default_partner_ids': active_id}</field>
<field name="context">{'default_partner_ids': [active_id]}</field>
</record>
<!-- open opportunities related to given partner -->

View File

@ -181,6 +181,7 @@
</group>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@ -241,7 +242,7 @@
<attribute name="invisible">False</attribute>
</xpath>
<xpath expr="//page[@name='page_history']" position="inside">
<group name="grp_claim" string="Claim">
<group name="grp_claim" string="Claims">
<field name="claims_ids" colspan="4" nolabel="1">
<tree string="Partners Claim" editable="bottom">
<field name="name"/>

View File

@ -61,10 +61,6 @@
on_change="onchange_partner_id(partner_id, email_from)"
/>
<field name="email_from"/>
<button name="remind_partner"
states="open,pending"
string="Send Reminder" type="object"
icon="gtk-go-forward" />
</group>
<group string="Categorization">
<field name="priority"/>
@ -99,6 +95,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -124,7 +124,7 @@
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Profiling">
<page string="Profiling" groups="base.group_user">
<button string="Use a questionnaire"
name="%(action_open_questionnaire)d" type="action" colspan="1"
icon="gtk-justify-fill" />

View File

@ -1,6 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_crm_profiling_answer_salesman,crm_profiling.answer.salesman,model_crm_profiling_answer,base.group_sale_salesman,1,1,1,0
access_crm_profiling_answer_manager,crm_profiling.answer.manager,model_crm_profiling_answer,base.group_sale_manager,1,1,1,1
access_crm_profiling_answer_user,crm_profiling.answer.user,model_crm_profiling_answer,base.group_user,1,0,0,0
access_crm_profiling_question_salesman,crm_profiling.question.salesman,model_crm_profiling_question,base.group_sale_salesman,1,1,1,0
access_crm_profiling_question_manager,crm_profiling.question.manager,model_crm_profiling_question,base.group_sale_manager,1,1,1,1
access_crm_profiling_questionnarie_salesman,crm_profiling.questionnarie.salesman,model_crm_profiling_questionnaire,base.group_sale_salesman,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_crm_profiling_answer_salesman crm_profiling.answer.salesman model_crm_profiling_answer base.group_sale_salesman 1 1 1 0
3 access_crm_profiling_answer_manager crm_profiling.answer.manager model_crm_profiling_answer base.group_sale_manager 1 1 1 1
4 access_crm_profiling_answer_user crm_profiling.answer.user model_crm_profiling_answer base.group_user 1 0 0 0
5 access_crm_profiling_question_salesman crm_profiling.question.salesman model_crm_profiling_question base.group_sale_salesman 1 1 1 0
6 access_crm_profiling_question_manager crm_profiling.question.manager model_crm_profiling_question base.group_sale_manager 1 1 1 1
7 access_crm_profiling_questionnarie_salesman crm_profiling.questionnarie.salesman model_crm_profiling_questionnaire base.group_sale_salesman 1 1 1 0

View File

@ -230,6 +230,10 @@
<field name="carrier_tracking_ref"/>
<field name="number_of_packages"/>
</field>
<field name="company_id" position="before">
<field name="weight"/>
<field name="weight_net" groups="base.group_no_one"/>
</field>
</field>
</record>
@ -307,13 +311,26 @@
<record id="view_delivery_order_inherit_stock" model="ir.ui.view">
<field name="name">stock.picking.out.form</field>
<field name="model">stock.picking.out</field>
<field name="inherit_id" ref="stock.view_picking_out_form"/>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="/form/header//button[@string='Create Invoice/Refund']" position="after">
<button name="%(report_shipping)d" string="Delivery Order" states="done" type="action" icon="gtk-print"/>
<xpath expr="/form/header//button[@name='action_process']" position="after">
<button name="%(report_shipping)d" string="Print Delivery Order" states="confirmed,assigned" type="action"/>
<button name="%(report_shipping)d" string="Print Delivery Order" states="done" type="action" class="oe_highlight"/>
</xpath>
</field>
</record>
<record id="view_picking_withcarrier_in_form" model="ir.ui.view">
<field name="name">delivery.stock.picking_withcarrier.in.form.view</field>
<field name="type">form</field>
<field name="model">stock.picking.in</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<field name="company_id" position="before">
<field name="weight"/>
<field name="weight_net" groups="base.group_no_one"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@ -202,5 +202,30 @@ class stock_picking_out(osv.osv):
'carrier_tracking_ref': fields.char('Carrier Tracking Ref', size=32),
'number_of_packages': fields.integer('Number of Packages'),
}
stock_picking_out()
class stock_picking_in(osv.osv):
_inherit = 'stock.picking.in'
def _cal_weight(self, cr, uid, ids, name, args, context=None):
return self.pool.get('stock.picking')._cal_weight(cr, uid, ids, name, args, context=context)
def _get_picking_line(self, cr, uid, ids, context=None):
return self.pool.get('stock.picking')._get_picking_line(cr, uid, ids, context=context)
_columns = {
'weight': fields.function(_cal_weight, type='float', string='Weight', digits_compute= dp.get_precision('Stock Weight'), multi='_cal_weight',
store={
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 20),
'stock.move': (_get_picking_line, ['product_id','product_qty','product_uom','product_uos_qty'], 20),
}),
'weight_net': fields.function(_cal_weight, type='float', string='Net Weight', digits_compute= dp.get_precision('Stock Weight'), multi='_cal_weight',
store={
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 20),
'stock.move': (_get_picking_line, ['product_id','product_qty','product_uom','product_uos_qty'], 20),
}),
}
stock_picking_in()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -39,7 +39,7 @@ class document_ftp_browse(osv.osv_memory):
data_pool = self.pool.get('ir.model.data')
aid = data_pool._get_id(cr, uid, 'document_ftp', 'action_document_browse')
aid = data_pool.browse(cr, uid, aid, context=context).res_id
ftp_url = self.pool.get('ir.actions.url').browse(cr, uid, aid, context=context)
ftp_url = self.pool.get('ir.actions.act_url').browse(cr, uid, aid, context=context)
url = ftp_url.url and ftp_url.url.split('ftp://') or []
if url:
url = url[1]

View File

@ -44,7 +44,7 @@ class document_ftp_configuration(osv.osv_memory):
# Update the action for FTP browse.
aid = data_pool._get_id(cr, uid, 'document_ftp', 'action_document_browse')
aid = data_pool.browse(cr, uid, aid, context=context).res_id
self.pool.get('ir.actions.url').write(cr, uid, [aid],
self.pool.get('ir.actions.act_url').write(cr, uid, [aid],
{'url': 'ftp://'+(conf.host or 'localhost:8021')+'/' + cr.dbname+'/'})
document_ftp_configuration()

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.actions.url" id="action_document_browse">
<record model="ir.actions.act_url" id="action_document_browse">
<field name="name">Browse Files</field>
<field name="url">ftp://localhost:8021/</field>
</record>

View File

@ -196,6 +196,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@ -246,12 +247,12 @@
<t t-name="kanban-box">
<div class="oe_module_vignette">
<a type="open" class="oe_module_icon">
<div class="oe_event_date "><t t-esc="record.date_begin.raw_value.getDate()"/></div>
<div class="oe_event_month_year">
<t t-esc="record.date_begin.raw_value.toString('MMM')"/>
<t t-esc="record.date_begin.raw_value.getFullYear()"/>
</div>
<div class="oe_event_time"><t t-esc="record.date_begin.raw_value.toString('hh:mm tt')"/></div>
<div class="oe_event_date "><t t-esc="record.date_begin.raw_value.getDate()"/></div>
<div class="oe_event_month_year">
<t t-esc="record.date_begin.raw_value.toString('MMM')"/>
<t t-esc="record.date_begin.raw_value.getFullYear()"/>
</div>
<div class="oe_event_time"><t t-esc="record.date_begin.raw_value.toString('hh:mm tt')"/></div>
</a>
<div class="oe_module_desc">
<h4><a type="open"><field name="name"/></a></h4>
@ -271,10 +272,10 @@
</p>
<t t-if="record.register_avail.raw_value != 0">
<t t-if="!record.is_subscribed.raw_value">
<input t-att-id="record.id.raw_value" type="text" name="subscribe" class="no_of_seats" value="1" onchange="document.getElementById('btn_sub' +this.id).setAttribute('data-context',JSON.stringify({'ticket':this.value}))"/>
<button t-att-id="'btn_sub'+record.id.raw_value" type="object" name="subscribe_to_event" class="oe_mail_button_follow">
<span >Subscribe</span>
</button>
<input t-att-id="record.id.raw_value" type="text" name="subscribe" class="no_of_seats" value="1" onchange="document.getElementById('btn_sub' +this.id).setAttribute('data-context',JSON.stringify({'ticket':this.value}))"/>
<button t-att-id="'btn_sub'+record.id.raw_value" type="object" name="subscribe_to_event" class="oe_mail_button_follow">
<span >Subscribe</span>
</button>
</t>
</t>
<t t-if="record.is_subscribed.raw_value">
@ -478,6 +479,7 @@
</group>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -16,7 +16,7 @@
<attribute name="invisible">False</attribute>
</xpath>
<xpath expr="//page[@name='page_history']" position="inside">
<group name="grp_event" string="Event">
<group name="grp_event" string="Events">
<field name="event_ids" colspan="4" nolabel="1">
<tree string="Events">
<field name="name" string="Event"/>
@ -24,7 +24,7 @@
</tree>
</field>
</group>
<group name="grp_registration" string="Registration">
<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"/>

View File

@ -195,7 +195,7 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS
strip_attachments=(not server.attach),
context=context)
if res_id and server.action_id:
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id], 'active_model': context.get("thread_model", False)})
imap_server.store(num, '+FLAGS', '\\Seen')
cr.commit()
count += 1
@ -220,7 +220,7 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS
strip_attachments=(not server.attach),
context=context)
if res_id and server.action_id:
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id], 'active_model': context.get("thread_model", False)})
pop_server.dele(num)
cr.commit()
_logger.info("fetched/processed %s email(s) on %s server %s", numMsgs, server.type, server.name)

View File

@ -205,25 +205,6 @@
</field>
</record>
<record id="open_view_employee_list_my_kanban" model="ir.actions.act_window.view">
<field name="sequence" eval="0"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="open_view_employee_list_my"/>
</record>
<record id="open_view_employee_list_my_tree2" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_employee_tree"/>
<field name="act_window_id" ref="open_view_employee_list_my"/>
</record>
<record id="open_view_employee_list_my_form2" model="ir.actions.act_window.view">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_employee_form"/>
<field name="act_window_id" ref="open_view_employee_list_my"/>
</record>
<menuitem action="open_view_employee_list_my" id="menu_open_view_employee_list_my" sequence="3" parent="menu_hr_main"/>
<record id="ir_ui_view_sc_employee" model="ir.ui.view_sc">
@ -372,14 +353,14 @@
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
</group>
</group>
<notebook>
<page string="Description">
<label for="description"/>
<field name="description"/>
<label for="requirements"/>
<field name="requirements"/>
</page>
</notebook>
<div>
<label for="description"/>
<field name="description"/>
</div>
<div>
<label for="requirements"/>
<field name="requirements"/>
</div>
</sheet>
</form>
</field>

View File

@ -178,7 +178,7 @@
<field nolabel="1" name="survey_request_ids">
<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,done,cancel" 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)]}"/>
<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)]}"/>
<button name="%(survey.survey_browse_response)d" string="Print Interview" type="action" states="done" icon="gtk-print" context="{'survey_id': survey_id, 'response_id' : [response], 'response_no':0,}" attrs="{'readonly':[('response','=',False)]}" />
</div>
<group>
@ -333,7 +333,7 @@
<field name="user_id" string="Interviewer"/>
<field name="user_to_review_id"/>
<field name="response" readonly="1" invisible="True"/>
<button name="%(survey.action_view_survey_question_message)d" string="Answer Survey" type="action" states="waiting_answer,done,cancel" 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)]}"/>
<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)]}"/>
<button name="action_print_survey" string="Print Survey" type="object" icon="gtk-print" context="{'survey_id': survey_id, 'response_id': [response], 'response_no':0}" attrs="{'readonly':[('survey_id','=',False)]}"/>
<button name="%(mail.action_email_compose_message_wizard)d" string="Send Reminder Email" icon="terp-mail-message-new" type="action" states="waiting_answer"/>
<field name="state"/>

View File

@ -139,6 +139,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -121,6 +121,7 @@
</group>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@ -158,6 +159,7 @@
<field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..."/>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -317,7 +317,10 @@ class hr_payslip(osv.osv):
'company_id': company_id,
'period_id': False,
'basic_before_leaves': 0.0,
'basic_amount': 0.0
'basic_amount': 0.0,
'number': '',
'payslip_run_id': False,
'paid': False,
})
return super(hr_payslip, self).copy(cr, uid, id, default, context=context)

View File

@ -234,7 +234,7 @@
</h2>
</div>
<group col="4">
<field name="contract_id" domain="[('employee_id','=',employee_id)]" on_change="onchange_contract_id(date_from, date_to, employee_id, contract_id)" context="{'default_employee_id': employee_id}"/>
<field name="contract_id" domain="[('employee_id','=',employee_id),('date_start','&lt;=',date_to),'|',('date_end','&gt;=',date_from),('date_end','=',False)]" on_change="onchange_contract_id(date_from, date_to, employee_id, contract_id)" context="{'default_employee_id': employee_id}"/>
<field name="number"/>
<field name="struct_id" attrs="{'required':[('contract_id','&lt;&gt;',False)]}"/>
<field name="name"/>

View File

@ -183,6 +183,7 @@
<field name="description" placeholder="Feedback of interviews..."/>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -8,5 +8,5 @@ access_survey_hr_user,survey.hr.user,survey.model_survey,base.group_hr_user,1,1,
access_crm_meeting_hruser,crm.meeting.hruser,base_calendar.model_crm_meeting,base.group_hr_user,1,1,1,1
access_hr_recruitment_source_hr_officer,hr.recruitment.source,model_hr_recruitment_source,base.group_hr_user,1,1,1,1
access_hr_recruitment_source_all,hr.recruitment.source,model_hr_recruitment_source,,1,0,0,0
access_hr_applicant_category,hr.applicant_category,model_hr_applicant_category,,1,0,0,0
access_hr_applicant_category,hr.applicant_category,model_hr_applicant_category,,1,1,1,0
access_hr_applicant_category_manager,hr.applicant_category,model_hr_applicant_category,base.group_hr_manager,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
8 access_crm_meeting_hruser crm.meeting.hruser base_calendar.model_crm_meeting base.group_hr_user 1 1 1 1
9 access_hr_recruitment_source_hr_officer hr.recruitment.source model_hr_recruitment_source base.group_hr_user 1 1 1 1
10 access_hr_recruitment_source_all hr.recruitment.source model_hr_recruitment_source 1 0 0 0
11 access_hr_applicant_category hr.applicant_category model_hr_applicant_category 1 0 1 0 1 0
12 access_hr_applicant_category_manager hr.applicant_category model_hr_applicant_category base.group_hr_manager 1 1 1 1

View File

@ -116,10 +116,12 @@
<field name="search_view_id" ref="hr_timesheet_line_search"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to your timesheets.
Click to record your timesheets.
</p><p>
Through this menu you can register and follow your workings
hours by project every day.
You can register and track your workings hours by project every
day. Every time spent on a project will become a cost in the
analytic accounting and can be re-invoiced to customers if
required.
</p>
</field>
</record>

View File

@ -1,8 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_analytic_timesheet,hr.analytic.timesheet,model_hr_analytic_timesheet,base.group_hr_user,1,1,1,1
access_hr_analytic_timesheet_employee,hr.analytic.timesheet,model_hr_analytic_timesheet,base.group_user,1,1,1,0
access_hr_analytic_timesheet_employee,hr.analytic.timesheet,model_hr_analytic_timesheet,base.group_user,1,1,1,1
access_hr_account_analytic_line,account.account.analytic.line,account.model_account_analytic_line,base.group_hr_user,1,1,1,1
access_hr_account_analytic_line_employee,account.account.analytic.line employee,account.model_account_analytic_line,base.group_user,1,1,1,0
access_hr_account_analytic_line_employee,account.account.analytic.line employee,account.model_account_analytic_line,base.group_user,1,1,1,1
access_account_analytic_journal,account.account.analytic.journal,account.model_account_analytic_journal,base.group_hr_user,1,1,1,1
access_product_product_user,product.product user,product.model_product_product,base.group_hr_user,1,1,1,1
access_product_template_hr_timesheet,product.template.hr.timesheet,product.model_product_template,base.group_hr_user,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_hr_analytic_timesheet hr.analytic.timesheet model_hr_analytic_timesheet base.group_hr_user 1 1 1 1
3 access_hr_analytic_timesheet_employee hr.analytic.timesheet model_hr_analytic_timesheet base.group_user 1 1 1 0 1
4 access_hr_account_analytic_line account.account.analytic.line account.model_account_analytic_line base.group_hr_user 1 1 1 1
5 access_hr_account_analytic_line_employee account.account.analytic.line employee account.model_account_analytic_line base.group_user 1 1 1 0 1
6 access_account_analytic_journal account.account.analytic.journal account.model_account_analytic_journal base.group_hr_user 1 1 1 1
7 access_product_product_user product.product user product.model_product_product base.group_hr_user 1 1 1 1
8 access_product_template_hr_timesheet product.template.hr.timesheet product.model_product_template base.group_hr_user 1 1 1 1

View File

@ -31,7 +31,7 @@ class report_timesheet_line(osv.osv):
'user_id': fields.many2one('res.users', 'User', readonly=True),
'date': fields.date('Date', readonly=True),
'day': fields.char('Day', size=128, readonly=True),
'quantity': fields.float('Quantity', readonly=True),
'quantity': fields.float('Time', readonly=True),
'cost': fields.float('Cost', readonly=True),
'product_id': fields.many2one('product.product', 'Product',readonly=True),
'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
@ -83,7 +83,7 @@ class report_timesheet_user(osv.osv):
_columns = {
'name': fields.char('Year',size=64,required=False, readonly=True),
'user_id':fields.many2one('res.users', 'User', readonly=True),
'quantity': fields.float('Quantity', readonly=True),
'quantity': fields.float('Time', readonly=True),
'cost': fields.float('Cost', readonly=True),
'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
@ -117,7 +117,7 @@ class report_timesheet_account(osv.osv):
'name': fields.char('Year',size=64,required=False, readonly=True),
'user_id':fields.many2one('res.users', 'User', readonly=True),
'account_id':fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
'quantity': fields.float('Quantity', readonly=True),
'quantity': fields.float('Time', readonly=True),
'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
@ -151,7 +151,7 @@ class report_timesheet_account_date(osv.osv):
'name': fields.char('Year',size=64,required=False, readonly=True),
'user_id':fields.many2one('res.users', 'User', readonly=True),
'account_id':fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
'quantity': fields.float('Quantity', readonly=True),
'quantity': fields.float('Time', readonly=True),
'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
}
@ -185,7 +185,7 @@ class report_timesheet_invoice(osv.osv):
'user_id':fields.many2one('res.users', 'User', readonly=True),
'account_id':fields.many2one('account.analytic.account', 'Project', readonly=True),
'manager_id':fields.many2one('res.users', 'Manager', readonly=True),
'quantity': fields.float('Quantity', readonly=True),
'quantity': fields.float('Time', readonly=True),
'amount_invoice': fields.float('To invoice', readonly=True)
}
_rec_name = 'user_id'

View File

@ -79,17 +79,24 @@ class account_analytic_line(osv.osv):
curr_invoice = {
'name': time.strftime('%d/%m/%Y')+' - '+account.name,
'partner_id': account.partner_id.id,
'company_id': account.company_id.id,
'payment_term': partner.property_payment_term.id or False,
'account_id': partner.property_account_receivable.id,
'currency_id': account.pricelist_id.currency_id.id,
'date_due': date_due,
'fiscal_position': account.partner_id.property_account_position.id
}
last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context)
invoices.append(last_invoice)
context2 = context.copy()
context2['lang'] = partner.lang
# set company_id in context, so the correct default journal will be selected
context2['force_company'] = curr_invoice['company_id']
# set force_company in context so the correct product properties are selected (eg. income account)
context2['company_id'] = curr_invoice['company_id']
last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context2)
invoices.append(last_invoice)
cr.execute("SELECT product_id, to_invoice, sum(unit_amount), product_uom_id, name " \
"FROM account_analytic_line as line " \
"WHERE account_id = %s " \
@ -115,11 +122,11 @@ class account_analytic_line(osv.osv):
else:
price = 0.0
general_account = product.product_tmpl_id.property_account_income or product.categ_id.property_account_income_categ
if not general_account:
raise osv.except_osv(_("Configuration Error!"), _("Please define income account for product '%s'.") % product.name)
taxes = product.taxes_id
tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id
if not account_id:
raise osv.except_osv(_("Configuration Error!"), _("Please define income account for product '%s'.") % product.name)
curr_line = {
'price_unit': price,
'quantity': qty,
@ -130,7 +137,7 @@ class account_analytic_line(osv.osv):
'product_id': product_id,
'invoice_line_tax_id': [(6,0,tax)],
'uos_id': uom,
'account_id': account_id,
'account_id': general_account.id,
'account_analytic_id': account.id,
}

View File

@ -43,7 +43,7 @@ class hr_timesheet_report(osv.osv):
'account_id': fields.many2one('account.analytic.account', 'Analytic Account',readonly=True),
'company_id': fields.many2one('res.company', 'Company',readonly=True),
'cost': fields.float('Cost',readonly=True, digits_compute=dp.get_precision('Account')),
'quantity': fields.float('Quantity',readonly=True),
'quantity': fields.float('Time',readonly=True),
}
def init(self, cr):

View File

@ -52,7 +52,7 @@ class timesheet_report(osv.osv):
('draft','Draft'),
('confirm','Confirmed'),
('done','Done')], 'Status', readonly=True),
'quantity': fields.float('#Quantity',readonly=True),
'quantity': fields.float('Time',readonly=True),
'cost': fields.float('#Cost',readonly=True),
}

View File

@ -77,6 +77,7 @@
<field name="description"/>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -10,6 +10,9 @@
<group>
<field name='name'/>
</group>
<footer>
<button string="Close" class="oe_highlight" special="cancel" />
</footer>
</form>
</field>
</record>

View File

@ -27,6 +27,7 @@ from dateutil import *
from pytz import timezone
from datetime import datetime
import time
import base64
from osv import *
from tools.translate import _
@ -282,7 +283,7 @@ class google_import(import_framework):
self.contact = self.gd_client.GetContactsFeed()
while self.contact:
for entry in self.contact.entry:
data = {}
data = {}
data['id'] = entry.id.text
name = tools.ustr(entry.title.text)
if name == "None":
@ -290,6 +291,8 @@ class google_import(import_framework):
data['name'] = name or _('Unknown')
emails = ','.join(email.address for email in entry.email)
data['email'] = emails
if self.gd_client.GetPhoto(entry):
data['image'] = base64.encodestring(self.gd_client.GetPhoto(entry))
if table == 'Contact':
data.update({'customer': str(self.context.get('customer')),
'supplier': str(self.context.get('supplier'))})
@ -322,6 +325,12 @@ class google_import(import_framework):
'name': value('company', fallback='name'),
'customer': 'customer',
'supplier': 'supplier',
'city': 'city',
'phone': 'phone',
'mobile': 'mobile',
'email': 'email',
'image': 'image',
'fax': 'fax',
'child_ids/id': ref(self.TABLE_ADDRESS, 'id'),
}
}

View File

@ -104,7 +104,7 @@ class synchronize_google(osv.osv_memory):
_columns = {
'customer': fields.boolean('Customer', help="Check this box to set newly created partner as Customer."),
'supplier': fields.boolean('Supplier', help="Check this box to set newly created partner as Supplier."),
'group_name': fields.selection(_get_group, "Group Name",help="Choose which group to import, By default it takes all."),
'group_name': fields.selection(_get_group, "Group Name", help="Choose which group to import, By default it takes all."),
'calendar_name': fields.selection(_get_calendars, "Calendar Name"),
}

View File

@ -7,7 +7,7 @@
<field name="arch" type="xml">
<form string="Import contacts from a google account" version="7.0">
<group colspan="4" col="4" width="430">
<field name="group_name" colspan="4"/>
<field name="group_name" colspan="4" required="1"/>
<newline/>
<group colspan="4" col="4">
<separator string="Partner Status for this Group:" colspan="4"/>
@ -32,7 +32,7 @@
<field name="arch" type="xml">
<form string="Import Google Calendar Events" version="7.0">
<group colspan="4" col="4" width="320">
<field name="calendar_name" />
<field name="calendar_name" required="1"/>
</group>
<footer>
<button name="import_google" string="_Import Events" type="object" class="oe_highlight" />

View File

@ -70,7 +70,7 @@ class partner_vat(osv.osv_memory):
FROM account_move_line l
LEFT JOIN res_partner p ON l.partner_id = p.id
LEFT JOIN account_tax_code c ON l.tax_code_id = c.id
WHERE c.code IN ('01','02','03','45','49')
WHERE c.code IN ('00','01','02','03','45','49')
AND l.partner_id IN %s
AND l.period_id IN %s
GROUP BY l.partner_id, p.name, p.vat) AS sub1

View File

@ -36,8 +36,9 @@ class WizardMultiChartsAccounts(osv.osv_memory):
def execute(self, cr, uid, ids, context=None):
"""Override of code in order to be able to link journal with account in XML"""
res = super(WizardMultiChartsAccounts, self).execute(cr, uid, ids, context)
path = addons.get_module_resource(os.path.join('l10n_ch','sterchi_chart','account_journal_rel.xml'))
path = addons.get_module_resource('l10n_ch','sterchi_chart','account_journal_rel.xml')
tools.convert_xml_import(cr, 'l10n_ch', path, idref=None, mode='init', noupdate=True, report=None)
res.update({'type': 'ir.actions.act_window_close'})
return res
WizardMultiChartsAccounts()

View File

@ -48,7 +48,7 @@ class ResPartnerBank(osv.osv):
'dta_code': fields.char('DTA code', size=5),
'print_bank': fields.boolean('Print Bank on BVR'),
'print_account': fields.boolean('Print Account Number on BVR'),
'acc_number': fields.char('Account/IBAN Number', size=64),
'acc_number': fields.char('Account Number', size=64),
'my_bank': fields.boolean('Use my account to print BVR ?', help="Check to print BVR invoices"),
}

View File

@ -28,34 +28,6 @@
</field>
</field>
</record>
<!-- res.partner.bank in res partner form-->
<!-- Partner -> Bank Details -->
<record model="ir.ui.view" id="l10nch_view_res_partner_bank_add_groups">
<field name="name">res.partner_bank.form.hide.f2</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account.view_partner_property_form"/>
<field name="arch" type="xml">
<field name="state" position="after">
<newline/>
<separator colspan="4" string="Account Infos"/>
<group string="Bank account info" colspan="4" attrs="{'invisible': [('state','in', ['bvpost','bvrpost'])]}" >
<field name="acc_number" string="Account/IBAN Number" attrs="{'required': [('state','not in',['bvpost','bvrpost'])]}"/>
<field name="dta_code"/>
<newline/>
</group>
<newline/>
<group string="Postal account info" colspan="4" attrs="{'invisible': [('state','not in', ['bvpost','bvrpost','bvbank','bvrbank'])]}" >
<field name="post_number" attrs="{'required': [('state','in',['bvpost','bvrpost','bvrbank'])]}"/>
<newline/>
</group>
<separator colspan="4" string="Financial Institute Infos"/>
<field name="bank" />
</field>
</field>
</record>
<!-- res.partner.bank base form-->
<!-- Sales -> Configuration -> Address Book -> Bank accounts -->
@ -66,35 +38,21 @@
<field name="model">res.partner.bank</field>
<field name="inherit_id" ref="base.view_partner_bank_form"/>
<field name="arch" type="xml">
<field name="state" position="after">
<newline/>
<separator colspan="4" string="Account Infos"/>
<newline/>
<group string="Bank account info" colspan="4" attrs="{'invisible': [('state','in', ['bvpost','bvrpost'])]}" >
<field name="acc_number" string="Account/IBAN Number" attrs="{'required': [('state','not in',['bvpost','bvrpost'])]}"/>
<field name="dta_code"/>
<newline/>
<xpath expr="//field[@name='acc_number']" position="attributes">
<attribute name="attrs">{'required': [('state','not in',['bvpost','bvrpost'])]}</attribute>
</xpath>
<xpath expr="//field[@name='acc_number']" position="after">
<field name="post_number" attrs="{'required': [('state','in',['bvpost','bvrpost','bvrbank'])], 'invisible': [('state','not in', ['bvpost','bvrpost','bvbank','bvrbank'])]}"/>
<field name="dta_code" attrs="{'invisible': [('state','in', ['bvpost','bvrpost'])]}"/>
<field name="my_bank" attrs="{'invisible': ['|', ('state', 'not in', ['bvrpost','bvrbank']), ('company_id', '=', False)]}" />
</xpath>
<xpath expr="//group[@name='bank']" position="after">
<group string="BVR print options" attrs="{'invisible': [('my_bank', '!=', True)]}" >
<field name="bvr_adherent_num" attrs="{'invisible': [('state','!=','bvrbank')]}"/>
<field name="print_bank"/>
<field name="print_account"/>
</group>
<newline/>
<group string="Postal account info" colspan="4" attrs="{'invisible': [('state','not in', ['bvpost','bvrpost','bvbank','bvrbank'])]}" >
<field name="post_number" attrs="{'required': [('state','in',['bvpost','bvrpost','bvrbank'])]}"/>
<newline/>
</group>
<separator colspan="4" string="Financial Institute Infos"/>
<newline/>
<group attrs="{'invisible': ['|', ('state', 'not in', ['bvrpost','bvrbank']), ('company_id', '=', False)]}" colspan="4">
<field name="my_bank" attrs="{'invisible': [('company_id', '=', False)]}" />
<group string="BVR print options" colspan="4" attrs="{'invisible': [('my_bank', '!=', True)]}" >
<field name="bvr_adherent_num" attrs="{'invisible': [('state','!=','bvrbank')]}"/>
<field name="print_bank"/>
<field name="print_account"/>
<newline/>
</group>
</group>
<newline/>
<field name="bank" />
</field>
</xpath>
</field>
</record>

View File

@ -277,7 +277,7 @@
<field name="type">view</field>
<field name="user_type" ref="account.data_account_type_liability"/>
<field name="reconcile" eval="False"/>
<field name="parent_id" ref="a1"/>
<field name="parent_id" ref="a20"/>
</record>
<!-- <record model="account.account.template" id="290">

View File

@ -3955,11 +3955,13 @@
<field name="code">4a</field>
<field name="parent_id" ref="btw_code_vanuit_buitenland"/>
<field name="name">Leveringen uit landen buiten de EU (invoer) (BTW)</field>
<field name="sign" eval="-1" />
</record>
<record id="btw_code_4b" model="account.tax.code.template">
<field name="code">4b</field>
<field name="parent_id" ref="btw_code_vanuit_buitenland"/>
<field name="name">Verwerving van goederen uit landen binnen de EU (BTW)</field>
<field name="sign" eval="-1" />
</record>
<!-- 5. Voorbelasting, kleineondernemersregeling, schatting en eindtotaal -->
<record id="btw_code_voorbelasting" model="account.tax.code.template">
@ -4100,6 +4102,7 @@
<field name="property_account_payable" ref="a_pay"/> <!-- crediteuren -->
<field name="property_account_expense_categ" ref="a_expense"/><!-- aankoop grondstoffen -->
<field name="property_account_income_categ" ref="a_sale"/> <!-- verkoop rekening -->
<field name="property_reserve_and_surplus_account" ref="a_9999"/>
</record>
<!-- BTW Template
@ -4325,7 +4328,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_I_6_1" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import binnen EU laag(1)</field>
<field eval="1.00" name="amount"/>
<field eval="-1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_I_6"/>
<field name="account_collected_id" ref="vat_payable6"/>
@ -4337,7 +4340,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_I_6_2" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import binnen EU laag(2)</field>
<field eval="-1.00" name="amount"/>
<field eval="1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_I_6"/>
<field name="account_collected_id" ref="vat_refund6"/>
@ -4360,7 +4363,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_I_19_1" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import binnen EU hoog(1)</field>
<field eval="1.00" name="amount"/>
<field eval="-1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_I_19"/>
<field name="account_collected_id" ref="vat_payable19"/>
@ -4372,7 +4375,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_I_19_2" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import binnen EU hoog(2)</field>
<field eval="-1.00" name="amount"/>
<field eval="1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_I_19"/>
<field name="account_collected_id" ref="vat_refund19"/>
@ -4395,7 +4398,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_I_overig_1" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import binnen EU overig(1)</field>
<field eval="1.00" name="amount"/>
<field eval="-1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_I_overig"/>
<field name="account_collected_id" ref="vat_payable19"/>
@ -4407,7 +4410,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_I_overig_2" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import binnen EU overig(2)</field>
<field eval="-1.00" name="amount"/>
<field eval="1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_I_overig"/>
<field name="account_collected_id" ref="vat_refund19"/>
@ -4458,7 +4461,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_E1_1" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import buiten EU laag(1)</field>
<field eval="1.00" name="amount"/>
<field eval="-1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_E1"/>
<field name="account_collected_id" ref="vat_payable6"/>
@ -4470,7 +4473,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_E1_2" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import buiten EU laag(2)</field>
<field eval="-1.00" name="amount"/>
<field eval="1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_E1"/>
<field name="account_collected_id" ref="vat_refund6"/>
@ -4493,7 +4496,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_E2_1" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import buiten EU hoog(1)</field>
<field eval="1.00" name="amount"/>
<field eval="-1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_E2"/>
<field name="account_collected_id" ref="vat_payable19"/>
@ -4505,7 +4508,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_E2_2" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import buiten EU hoog(2)</field>
<field eval="-1.00" name="amount"/>
<field eval="1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_E2"/>
<field name="account_collected_id" ref="vat_refund19"/>
@ -4528,7 +4531,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_E_overig_1" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import buiten EU overig(1)</field>
<field eval="1.00" name="amount"/>
<field eval="-1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_E_overig"/>
<field name="account_collected_id" ref="vat_payable19"/>
@ -4540,7 +4543,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
<record id="btw_E_overig_2" model="account.tax.template">
<field name="chart_template_id" ref="l10nnl_chart_template"/>
<field name="name">Inkopen import buiten EU overig(2)</field>
<field eval="-1.00" name="amount"/>
<field eval="1.00" name="amount"/>
<field name="type">percent</field>
<field name="parent_id" ref="btw_E_overig"/>
<field name="account_collected_id" ref="vat_refund19"/>

View File

@ -19,6 +19,7 @@
#
##############################################################################
from openerp import SUPERUSER_ID
from osv import osv
from osv import fields
import tools
@ -91,7 +92,7 @@ class mail_notification(osv.Model):
:param browse_record message: mail.message to notify
"""
notify_pids = []
for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context):
for partner in self.pool.get('res.partner').browse(cr, SUPERUSER_ID, partner_ids, context=context):
# Do not send an email to the writer
if partner.user_ids and partner.user_ids[0].id == uid:
continue

View File

@ -19,9 +19,10 @@
#
##############################################################################
from openerp import SUPERUSER_ID
from osv import osv
from osv import fields
from tools.translate import _
class ir_ui_menu(osv.osv):
""" Override of ir.ui.menu class. When adding mail_thread module, each
@ -38,13 +39,14 @@ class ir_ui_menu(osv.osv):
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
""" Override to take off menu entries (mail.group) the user is not
following. """
following. Access are done using SUPERUSER_ID to avoid access
rights issues for an internal back-end algorithm. """
ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0, limit=None, order=order, context=context, count=False)
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
follower_obj = self.pool.get('mail.followers')
for menu in self.browse(cr, uid, ids, context=context):
if menu.mail_group_id:
sub_ids = follower_obj.search(cr, uid, [
sub_ids = follower_obj.search(cr, SUPERUSER_ID, [
('partner_id', '=', partner_id), ('res_model', '=', 'mail.group'),
('res_id', '=', menu.mail_group_id.id)
], context=context)

View File

@ -81,6 +81,7 @@
</group>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"
options='{"thread_level": 1}'/>
<field name="message_follower_ids" widget="mail_followers"/>

View File

@ -19,13 +19,12 @@
#
##############################################################################
# import ast
import base64
import logging
import tools
from osv import osv
from osv import fields
from openerp import SUPERUSER_ID
from osv import osv, fields
from tools.translate import _
_logger = logging.getLogger(__name__)
@ -134,7 +133,8 @@ class mail_mail(osv.Model):
:return: True
"""
if mail.auto_delete:
mail.unlink()
# done with SUPERUSER_ID to avoid giving large unlink access rights
self.unlink(cr, SUPERUSER_ID, [mail.id], context=context)
return True
def send_get_mail_subject(self, cr, uid, mail, force=False, partner=None, context=None):

View File

@ -24,8 +24,9 @@ import openerp
import tools
from email.header import decode_header
from openerp import SUPERUSER_ID
from operator import itemgetter
from osv import osv, fields
from osv import osv, orm, fields
from tools.translate import _
_logger = logging.getLogger(__name__)
@ -62,14 +63,14 @@ class mail_message(osv.Model):
continue
try:
result[message.id] = self._shorten_name(self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1])
except openerp.exceptions.AccessDenied, e:
except (orm.except_orm, osv.except_osv):
pass
return result
def _get_unread(self, cr, uid, ids, name, arg, context=None):
""" Compute if the message is unread by the current user. """
res = dict((id, {'unread': False}) for id in ids)
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
notif_obj = self.pool.get('mail.notification')
notif_ids = notif_obj.search(cr, uid, [
('partner_id', 'in', [partner_id]),
@ -87,7 +88,7 @@ class mail_message(osv.Model):
read_cond = '(read = false or read is null)'
else:
read_cond = 'read = true'
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
cr.execute("SELECT message_id FROM mail_notification "\
"WHERE partner_id = %%s AND %s" % read_cond,
(partner_id,))
@ -140,7 +141,8 @@ class mail_message(osv.Model):
return []
def _get_default_author(self, cr, uid, context=None):
return self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
# remove context to avoid possible hack in browse with superadmin using context keys that could trigger a specific behavior
return self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=None).partner_id.id
_defaults = {
'type': 'email',
@ -154,16 +156,17 @@ class mail_message(osv.Model):
#------------------------------------------------------
def vote_toggle(self, cr, uid, ids, user_ids=None, context=None):
''' Toggles voting '''
''' Toggles voting. Done as SUPERUSER_ID because of write access on
mail.message not always granted. '''
if not user_ids:
user_ids = [uid]
for message in self.read(cr, uid, ids, ['vote_user_ids'], context=context):
for user_id in user_ids:
has_voted = user_id in message.get('vote_user_ids')
if not has_voted:
self.write(cr, uid, message.get('id'), {'vote_user_ids': [(4, user_id)]}, context=context)
self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(4, user_id)]}, context=context)
else:
self.write(cr, uid, message.get('id'), {'vote_user_ids': [(3, user_id)]}, context=context)
self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(3, user_id)]}, context=context)
return True
#------------------------------------------------------
@ -171,17 +174,31 @@ class mail_message(osv.Model):
#------------------------------------------------------
def _message_dict_get(self, cr, uid, msg, context=None):
""" Return a dict representation of the message browse record. """
""" Return a dict representation of the message browse record. A read
is performed to because of access rights issues (reading many2one
fields allow to have the foreign record name without having
to check external access rights).
"""
has_voted = False
vote_ids = self.pool.get('res.users').name_get(cr, uid, [user.id for user in msg.vote_user_ids], context=context)
vote_ids = self.pool.get('res.users').name_get(cr, SUPERUSER_ID, [user.id for user in msg.vote_user_ids], context=context)
for vote in vote_ids:
if vote[0] == uid:
has_voted = True
break
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context)]
author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0]
author_user_id = self.pool.get('res.users').name_get(cr, uid, [msg.author_id.user_ids[0].id], context=context)[0]
partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context)
try:
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context)]
except (orm.except_orm, osv.except_osv):
attachment_ids = []
try:
author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0]
is_author = uid in msg.author_id.user_ids
except (orm.except_orm, osv.except_osv):
author_id = False
is_author = False
try:
partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context)
except (orm.except_orm, osv.except_osv):
partner_ids = []
return {
'id': msg.id,
'type': msg.type,
@ -193,7 +210,7 @@ class mail_message(osv.Model):
'subject': msg.subject,
'date': msg.date,
'author_id': author_id,
'author_user_id': author_user_id,
'is_author': is_author,
'partner_ids': partner_ids,
'child_ids': [],
'vote_user_ids': vote_ids,
@ -246,7 +263,7 @@ class mail_message(osv.Model):
limit = limit or self._message_read_limit
context = context or {}
if not ids:
ids = self.search(cr, uid, domain, context=context, limit=limit)
ids = self.search(cr, SUPERUSER_ID, domain, context=context, limit=limit)
messages = self.browse(cr, uid, ids, context=context)
result = []
@ -294,41 +311,99 @@ class mail_message(osv.Model):
cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""")
def check_access_rule(self, cr, uid, ids, operation, context=None):
""" mail.message access rule check
- message received (a notification exists) -> ok
- check rules of related document if exists
- fallback on normal mail.message check """
""" Access rules of mail.message:
- read: if
- notification exist (I receive pushed message) OR
- author_id = pid (I am the author) OR
- I can read the related document if res_model, res_id
- Otherwise: raise
- create: if
- I am in the document message_follower_ids OR
- I can write on the related document if res_model, res_id
- Otherwise: raise
- write: if
- I can write on the related document if res_model, res_id
- Otherwise: raise
- unlink: if
- I can write on the related document if res_model, res_id
- Otherwise: raise
"""
if uid == SUPERUSER_ID:
return
if isinstance(ids, (int, long)):
ids = [ids]
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
# check messages for which you have a notification
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
not_obj = self.pool.get('mail.notification')
not_ids = not_obj.search(cr, uid, [
('partner_id', '=', partner_id),
('message_id', 'in', ids),
], context=context)
notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, uid, not_ids, context=context)
if notification.message_id.id in ids]
# check messages linked to an existing document
# Read mail_message.ids to have their values
model_record_ids = {}
document_ids = []
cr.execute('SELECT DISTINCT id, model, res_id FROM mail_message WHERE id = ANY (%s)', (ids,))
for id, rmod, rid in cr.fetchall():
if not (rmod and rid):
continue
document_ids.append(id)
model_record_ids.setdefault(rmod, set()).add(rid)
message_values = dict.fromkeys(ids)
cr.execute('SELECT DISTINCT id, model, res_id, author_id FROM "%s" WHERE id = ANY (%%s)' % self._table, (ids,))
for id, rmod, rid, author_id in cr.fetchall():
message_values[id] = {'res_model': rmod, 'res_id': rid, 'author_id': author_id}
if rmod:
model_record_ids.setdefault(rmod, set()).add(rid)
# Read: Check for received notifications -> could become an ir.rule, but not till we do not have a many2one variable field
if operation == 'read':
not_obj = self.pool.get('mail.notification')
not_ids = not_obj.search(cr, SUPERUSER_ID, [
('partner_id', '=', partner_id),
('message_id', 'in', ids),
], context=context)
notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)]
else:
notified_ids = []
# Read: Check messages you are author -> could become an ir.rule, but not till we do not have a many2one variable field
if operation == 'read':
author_ids = [mid for mid, message in message_values.iteritems()
if message.get('author_id') and message.get('author_id') == partner_id]
else:
author_ids = []
# Create: Check message_follower_ids
if operation == 'create':
doc_follower_ids = []
for model, mids in model_record_ids.items():
fol_obj = self.pool.get('mail.followers')
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
('res_model', '=', model),
('res_id', 'in', list(mids)),
('partner_id', '=', partner_id),
], context=context)
fol_mids = [follower.res_id for follower in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)]
doc_follower_ids += [mid for mid, message in message_values.iteritems()
if message.get('res_model') == model and message.get('res_id') in fol_mids]
else:
doc_follower_ids = []
# Calculate remaining ids, and related model/res_ids
model_record_ids = {}
other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids))
for id in other_ids:
if message_values[id]['res_model']:
model_record_ids.setdefault(message_values[id]['res_model'], set()).add(message_values[id]['res_id'])
# CRUD: Access rights related to the document
document_related_ids = []
for model, mids in model_record_ids.items():
model_obj = self.pool.get(model)
mids = model_obj.exists(cr, uid, mids)
model_obj.check_access_rights(cr, uid, operation)
model_obj.check_access_rule(cr, uid, mids, operation, context=context)
if operation in ['create', 'write', 'unlink']:
model_obj.check_access_rights(cr, uid, 'write')
model_obj.check_access_rule(cr, uid, mids, 'write', context=context)
else:
model_obj.check_access_rights(cr, uid, operation)
model_obj.check_access_rule(cr, uid, mids, operation, context=context)
document_related_ids += [mid for mid, message in message_values.iteritems()
if message.get('res_model') == model and message.get('res_id') in mids]
# fall back on classic operation for other ids
other_ids = set(ids).difference(set(notified_ids), set(document_ids))
super(mail_message, self).check_access_rule(cr, uid, other_ids, operation, context=None)
# Calculate remaining ids: if not void, raise an error
other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids), set(document_related_ids))
if not other_ids:
return
raise orm.except_orm(_('Access Denied'),
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
(self._description, operation))
def create(self, cr, uid, values, context=None):
if not values.get('message_id') and values.get('res_id') and values.get('model'):
@ -337,6 +412,13 @@ class mail_message(osv.Model):
self.notify(cr, uid, newid, context=context)
return newid
def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
""" Override to explicitely call check_access_rule, that is not called
by the ORM. It instead directly fetches ir.rules and apply them. """
res = super(mail_message, self).read(cr, uid, ids, fields=fields, context=context, load=load)
self.check_access_rule(cr, uid, ids, 'read', context=context)
return res
def unlink(self, cr, uid, ids, context=None):
# cascade-delete attachments that are directly attached to the message (should only happen
# for mail.messages that act as parent for a standalone mail.mail record).
@ -360,12 +442,14 @@ class mail_message(osv.Model):
partners_to_notify |= set(partner.id for partner in message.partner_ids)
# add all followers and set add them in partner_ids
if message.model and message.res_id:
record = self.pool.get(message.model).browse(cr, uid, message.res_id, context=context)
record = self.pool.get(message.model).browse(cr, SUPERUSER_ID, message.res_id, context=context)
extra_notified = set(partner.id for partner in record.message_follower_ids)
missing_notified = extra_notified - partners_to_notify
if missing_notified:
message.write({'partner_ids': [(4, p_id) for p_id in missing_notified]})
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(4, p_id) for p_id in missing_notified]}, context=context)
partners_to_notify |= extra_notified
# # remove uid from partners
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(3, uid)]}, context=context)
self.pool.get('mail.notification').notify(cr, uid, list(partners_to_notify), newid, context=context)
def copy(self, cr, uid, id, default=None, context=None):

View File

@ -76,13 +76,6 @@
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_message_search"/>
</record>
<act_window domain="[('partner_id', '=', active_id), ('email_from', '!=', False)]"
context="{'default_partner_id': active_id}"
id="act_res_partner_emails" name="Emails"
res_model="mail.message"
src_model="res.partner"
view_id="view_message_tree"/>
<!-- Add menu entry in Settings/Email -->
<menuitem name="Messages" id="menu_mail_message" parent="base.menu_email" action="action_view_mail_message"/>
@ -95,7 +88,7 @@
</record>
<record id="action_mail_my_feeds" model="ir.actions.client">
<field name="name">My Feeds</field>
<field name="name">My Posts</field>
<field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('author_id.user_ids', 'in', [uid])],
'context': {'default_model': 'res.users', 'default_res_id': uid} }&quot;"/>

View File

@ -30,6 +30,7 @@ import xmlrpclib
from email.message import Message
from mail_message import decode
from openerp import SUPERUSER_ID
from osv import osv, fields
from tools.safe_eval import safe_eval as eval
@ -118,26 +119,27 @@ class mail_thread(osv.AbstractModel):
def _get_message_data(self, cr, uid, ids, name, args, context=None):
res = dict((id, dict(message_unread=False, message_summary='')) for id in ids)
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
# search for unread messages, by reading directly mail.notification, as SUPERUSER
notif_obj = self.pool.get('mail.notification')
notif_ids = notif_obj.search(cr, uid, [
notif_ids = notif_obj.search(cr, SUPERUSER_ID, [
('partner_id.user_ids', 'in', [uid]),
('message_id.res_id', 'in', ids),
('message_id.model', '=', self._name),
('read', '=', False)
], context=context)
for notif in notif_obj.browse(cr, uid, notif_ids, context=context):
for notif in notif_obj.browse(cr, SUPERUSER_ID, notif_ids, context=context):
res[notif.message_id.res_id]['message_unread'] = True
for thread in self.browse(cr, uid, ids, context=context):
cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or ''
res[thread.id]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids))
res[thread.id]['message_is_follower'] = user.partner_id.id in [follower.id for follower in thread.message_follower_ids]
for thread in self.read(cr, uid, ids, ['message_follower_ids', 'message_comment_ids', 'message_ids'], context=context):
cls = res[thread['id']]['message_unread'] and ' class="oe_kanban_mail_new"' or ''
res[thread['id']]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread['message_comment_ids']), len(thread['message_follower_ids']))
res[thread['id']]['message_is_follower'] = partner_id in thread['message_follower_ids']
return res
def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None):
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
res = {}
notif_obj = self.pool.get('mail.notification')
notif_ids = notif_obj.search(cr, uid, [
@ -196,6 +198,13 @@ class mail_thread(osv.AbstractModel):
fol_obj.unlink(cr, uid, fol_ids, context=context)
return super(mail_thread, self).unlink(cr, uid, ids, context=context)
def copy(self, cr, uid, id, default=None, context=None):
default = default or {}
default['message_ids'] = []
default['message_comment_ids'] = []
default['message_follower_ids'] = []
return super(mail_thread, self).copy(cr, uid, id, default=default, context=context)
#------------------------------------------------------
# mail.message wrappers and tools
#------------------------------------------------------
@ -620,7 +629,7 @@ class mail_thread(osv.AbstractModel):
""" Wrapper on message_subscribe, using users. If user_ids is not
provided, subscribe uid instead. """
if not user_ids: user_ids = [uid]
partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)]
partner_ids = [user['partner_id'][0] for user in self.pool.get('res.users').read(cr, uid, user_ids, ['partner_id'], context=context)]
return self.message_subscribe(cr, uid, ids, partner_ids, context=context)
def message_subscribe(self, cr, uid, ids, partner_ids, context=None):
@ -631,7 +640,7 @@ class mail_thread(osv.AbstractModel):
""" Wrapper on message_subscribe, using users. If user_ids is not
provided, unsubscribe uid instead. """
if not user_ids: user_ids = [uid]
partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)]
partner_ids = [user['partner_id'][0] for user in self.pool.get('res.users').read(cr, uid, user_ids, ['partner_id'], context=context)]
return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context)
def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None):

View File

@ -9,6 +9,7 @@
<field name="arch" type="xml">
<xpath expr="//sheet" position="after">
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"
options='{"thread_level": 1}'/>
<field name="message_follower_ids" widget="mail_followers"/>

View File

@ -1,13 +1,14 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_global,mail.mail,model_mail_mail,base.group_system,1,1,1,1
access_mail_message_all,mail.message.all,model_mail_message,,1,0,0,0
access_mail_message_all,mail.message.all,model_mail_message,,1,0,1,0
access_mail_message_group_user,mail.message.group.user,model_mail_message,base.group_user,1,1,1,1
access_mail_thread,mail.thread,model_mail_thread,base.group_user,1,1,1,0
access_mail_followers_all,mail.followers.all,model_mail_followers,,1,1,1,1
access_mail_notification_all,mail.notification.all,model_mail_notification,,1,1,1,1
access_mail_group,mail.group,model_mail_group,base.group_user,1,1,1,1
access_mail_mail_all,mail.mail.all,model_mail_mail,,0,0,1,0
access_mail_mail_system,mail.mail.system,model_mail_mail,base.group_system,1,1,1,1
access_mail_followers_all,mail.followers.all,model_mail_followers,,0,0,0,0
access_mail_followers_system,mail.followers.system,model_mail_followers,base.group_system,1,1,1,1
access_mail_notification_all,mail.notification.all,model_mail_notification,,1,0,0,0
access_mail_notification_aystem,mail.notification.system,model_mail_notification,base.group_system,1,1,1,1
access_mail_group_all,mail.group.all,model_mail_group,,1,0,0,0
access_mail_group_user,mail.group.user,model_mail_group,base.group_user,1,1,1,1
access_mail_alias_all,mail.alias.all,model_mail_alias,,1,0,0,0
access_mail_alias_user,mail.alias,model_mail_alias,base.group_user,1,1,1,0
access_mail_alias_system,mail.alias,model_mail_alias,base.group_system,1,1,1,1
access_mail_mail_user,mail.mail,model_mail_mail,base.group_user,1,1,1,0
access_mail_mail_manager,mail.mail,model_mail_mail,group_mail_manager,1,1,1,1
access_mail_vote_all,mail.vote.all,model_mail_vote,,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mail_global access_mail_message_all mail.mail mail.message.all model_mail_mail model_mail_message base.group_system 1 1 0 1 1 0
access_mail_message_all mail.message.all model_mail_message 1 0 0 0
3 access_mail_message_group_user mail.message.group.user model_mail_message base.group_user 1 1 1 1
4 access_mail_thread access_mail_mail_all mail.thread mail.mail.all model_mail_thread model_mail_mail base.group_user 1 0 1 0 1 0
5 access_mail_followers_all access_mail_mail_system mail.followers.all mail.mail.system model_mail_followers model_mail_mail base.group_system 1 1 1 1
6 access_mail_notification_all access_mail_followers_all mail.notification.all mail.followers.all model_mail_notification model_mail_followers 1 0 1 0 1 0 1 0
7 access_mail_group access_mail_followers_system mail.group mail.followers.system model_mail_group model_mail_followers base.group_user base.group_system 1 1 1 1
8 access_mail_notification_all mail.notification.all model_mail_notification 1 0 0 0
9 access_mail_notification_aystem mail.notification.system model_mail_notification base.group_system 1 1 1 1
10 access_mail_group_all mail.group.all model_mail_group 1 0 0 0
11 access_mail_group_user mail.group.user model_mail_group base.group_user 1 1 1 1
12 access_mail_alias_all mail.alias.all model_mail_alias 1 0 0 0
13 access_mail_alias_user mail.alias model_mail_alias base.group_user 1 1 1 0
access_mail_alias_system mail.alias model_mail_alias base.group_system 1 1 1 1
access_mail_mail_user mail.mail model_mail_mail base.group_user 1 1 1 0
access_mail_mail_manager mail.mail model_mail_mail group_mail_manager 1 1 1 1
14 access_mail_vote_all mail.vote.all model_mail_vote 1 1 1 1

View File

@ -2,20 +2,6 @@
<openerp>
<data>
<!-- CATEGORY -->
<record model="ir.module.category" id="module_category_social">
<field name="name">Home</field>
<field name="parent_id" ref="base.module_category_tools"/>
<field name="sequence">26</field>
</record>
<!-- GROUPS -->
<record id="group_mail_manager" model="res.groups">
<field name="name">Mail manager</field>
<field name="comment"></field>
<field name="category_id" ref="module_category_social"/>
</record>
<!-- RULES -->
<record id="group_rule_public_and_joined" model="ir.rule">
<field name="name">Mail.group: access only public and joined groups</field>
@ -24,5 +10,18 @@
<field name="domain_force">['|', '|', ('public', '=', 'public'), ('message_follower_ids', 'in', [user.id]), '&amp;', ('public','=','groups'), ('group_public_id','in', [x.id for x in user.groups_id])]</field>
</record>
<!--
This rule can not be uncommented, because we have a more wide method in mail.message. When we implement a many2one_variable field, we will be able to uncomment this.
<record id="mail_message_read_partner_or_author" model="ir.rule">
<field name="name">mail.message: read if notified or author</field>
<field name="model_id" ref="model_mail_message"/>
<field name="domain_force">['|', ('partner_ids', 'in', user.partner_id.id), ('author_id', '=', user.partner_id.id)]</field>
<field name="perm_create" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
-->
</data>
</openerp>

View File

@ -124,6 +124,14 @@
}
/* ------------------------------------------------------------ */
/* Followers
/* ------------------------------------------------------------ */
.openerp div.oe_mail_recthread_aside h4 {
display: inline-block;
}
/* ------------------------------------------------------------ */
/* Thread
/* ------------------------------------------------------------ */
@ -224,7 +232,7 @@
clear: both;
}
.openerp .oe_mail_msg_content a {
.openerp .oe_chatter a {
cursor: pointer;
}
@ -258,30 +266,6 @@
display: none;
}
/*--------------------------------------------------------------*/
/* mail.vote
/*--------------------------------------------------------------*/
.openerp .oe_mail_msg_content button.oe_mail_msg_vote {
height:21px;
width: 30px;
padding: 1px;
background: #8A89BA;
margin-top: -4px;
}
.openerp .oe_mail_msg_content button.oe_mail_msg_vote_true {
background:#DC5F59;
}
.openerp .oe_mail_msg_content button.oe_mail_msg_vote span {
color: white;
}
.openerp .oe_mail_msg_content span.oe_mail_vote_count{
color: #807FB4;
}
/* ------------------------------------------------------------ */
/* mail.compose.message form view & OpenERP hacks

View File

@ -56,11 +56,6 @@ openerp.mail = function(session) {
return session.origin + '/web/binary/saveas?session_id=' + session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id=' + attachment['id'];
},
/** Check if the current user is the message author */
is_author: function (widget, message_user_id) {
return (widget.session && widget.session.uid != 0 && widget.session.uid == message_user_id);
},
/** Replaces some expressions
* - :name - shortcut to an image
*/
@ -301,6 +296,7 @@ openerp.mail = function(session) {
// TDE TODO: check for deferred, not sure it is correct
this._super.apply(this, arguments);
this.bind_events();
this.display_user_avatar();
// fetch and display message, using message_ids if set
var display_done = $.when(this.message_fetch(true, [], {})).then(this.proxy('do_customize_display'));
// add message composition form view
@ -313,7 +309,6 @@ openerp.mail = function(session) {
/** Customize the display
* - show_header_compose: show the composition form in the header */
do_customize_display: function() {
this.display_user_avatar();
if (this.options.show_header_compose) {
this.$el.find('div.oe_mail_thread_action').eq(0).show();
}
@ -491,8 +486,7 @@ openerp.mail = function(session) {
* - record.date: formatting according to the user timezone
* - record.timerelative: relative time givein by timeago lib
* - record.avatar: image url
* - record.attachment_ids[].url: url of each attachment
* - record.is_author: is the current user the author of the record */
* - record.attachment_ids[].url: url of each attachment */
display_record: function (record) {
// formatting and additional fields
record.date = session.web.format_value(record.date, {type:"datetime"});
@ -506,7 +500,6 @@ openerp.mail = function(session) {
var attach = record.attachment_ids[l];
attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
}
record.is_author = mail.ChatterUtils.is_author(this, record.author_user_id[0]);
// add to internal storage
this.records[record.id] = record;
// render, add the expand feature
@ -649,12 +642,15 @@ openerp.mail = function(session) {
// update domain
var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]);
// create and render Thread widget
var show_header_compose = this.view.is_action_enabled('edit') ||
(this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
this.$el.find('div.oe_mail_recthread_main').empty();
var thread = new mail.Thread(self, domain, this.options.context,
{ 'thread_level': this.options.thread_level,
'use_composer': true,
'show_header_compose': show_header_compose,
'use_composer': show_header_compose,
'show_dd_delete': true,
'show_reply_by_email': true });
'show_reply_by_email': show_header_compose });
return thread.appendTo(this.$el.find('div.oe_mail_recthread_main'));
},
});

View File

@ -45,8 +45,8 @@ openerp_mail_followers = function(session, mail) {
},
reinit: function() {
this.$el.find('button.oe_mail_button_follow').hide();
this.$el.find('button.oe_mail_button_unfollow').hide();
this.message_is_follower == undefined;
this.display_buttons();
},
bind_events: function() {
@ -55,7 +55,7 @@ openerp_mail_followers = function(session, mail) {
.mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
this.$el.on('click', 'button.oe_mail_button_follow', function () { self.do_follow(); });
this.$el.on('click', 'button.oe_mail_button_invite', function(event) {
this.$el.on('click', 'a.oe_mail_invite', function(event) {
action = {
type: 'ir.actions.act_window',
res_model: 'mail.wizard.invite',
@ -74,41 +74,65 @@ openerp_mail_followers = function(session, mail) {
read_value: function() {
var self = this;
return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).pipe(function (results) {
return results[0].message_follower_ids;
}).pipe(this.proxy('set_value'));
return this.ds_model.read_ids([this.view.datarecord.id], ['message_is_follower', 'message_follower_ids']).then(function (results) {
self.set_value(results[0].message_follower_ids, results[0].message_is_follower);
});
},
set_value: function(value_) {
set_value: function(value_, message_is_follower) {
this.reinit();
if (! this.view.datarecord.id ||
session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
this.$el.find('div.oe_mail_recthread_aside').hide();
this.$('div.oe_mail_recthread_aside').hide();
return;
}
return this.fetch_followers(value_ || this.get_value());
return this.fetch_followers(value_ || this.get_value(), message_is_follower);
},
fetch_followers: function (value_) {
return this.ds_follow.call('read', [value_, ['name', 'user_ids']]).pipe(this.proxy('display_followers'));
fetch_followers: function (value_, message_is_follower) {
this.value = value_;
this.message_is_follower = message_is_follower || (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
return this.ds_follow.call('read', [value_, ['name', 'user_ids']]).pipe(this.proxy('display_followers'), this.proxy('display_generic'));
},
/* Display generic info about follower, for people not having access to res_partner */
display_generic: function (error, event) {
event.preventDefault();
var node_user_list = this.$('ul.oe_mail_followers_display').empty();
// format content: Followers (You and 0 other) // Followers (3)
var content = this.options.title;
if (this.message_is_follower) {
content += ' (You and ' + (this.value.length-1) + ' other)';
}
else {
content += ' (' + this.value.length + ')'
}
this.$('div.oe_mail_recthread_followers h4').html(content);
this.display_buttons();
return $.when();
},
/** Display the followers, evaluate is_follower directly */
display_followers: function (records) {
var self = this;
this.message_is_follower = _.indexOf(_.flatten(_.pluck(records, 'user_ids')), this.session.uid) != -1;
var node_user_list = this.$el.find('ul.oe_mail_followers_display').empty();
this.$el.find('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')');
var node_user_list = this.$('ul.oe_mail_followers_display').empty();
this.$('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')');
_(records).each(function (record) {
record.avatar_url = mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record.id);
$(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(node_user_list);
});
if (this.message_is_follower) {
this.$el.find('button.oe_mail_button_follow').hide();
this.$el.find('button.oe_mail_button_unfollow').show(); }
else {
this.$el.find('button.oe_mail_button_follow').show();
this.$el.find('button.oe_mail_button_unfollow').hide(); }
this.display_buttons();
},
display_buttons: function () {
this.$('button.oe_mail_button_follow').hide();
this.$('button.oe_mail_button_unfollow').hide();
this.$('span.oe_mail_invite_wrapper').hide();
if (! this.view.is_action_enabled('edit')) return;
this.$('span.oe_mail_invite_wrapper').show();
if (this.message_is_follower) { this.$('button.oe_mail_button_unfollow').show(); }
else if (this.message_is_follower == false) { this.$('button.oe_mail_button_follow').show(); }
},
do_follow: function () {

View File

@ -140,7 +140,7 @@
<li t-if="options.show_record_name and record.record_name and record.subject and options.thread_level > 0">
<a t-attf-href="#model=#{record.model}&amp;id=#{record.res_id}"><t t-raw="record.record_name"/></a>
</li>
<li><a t-attf-href="#model=res.partner&amp;id=#{record.author_id[0]}"><t t-raw="record.author_id[1]"/></a></li>
<li t-if="record.author_id"><a t-attf-href="#model=res.partner&amp;id=#{record.author_id[0]}"><t t-raw="record.author_id[1]"/></a></li>
<li><span t-att-title="record.date"><t t-raw="record.timerelative"/></span></li>
<t t-call="mail.thread.message.vote"/>
<li t-if="options.show_reply"><a class="oe_mail_msg_reply">Reply</a></li>
@ -185,7 +185,7 @@
<t t-if='record.vote_user_ids.length > 0'>
<span class="oe_left oe_mail_vote_count"><t t-esc="record.vote_user_ids.length"/> votes</span>
</t>
<button t-attf-class="oe_mail_msg_vote oe_mail_msg_vote_#{record.has_voted}" t-attf-data-msg_id="{record.id}">
<button t-attf-class="oe_mail_msg_vote oe_tag" t-attf-data-msg_id="{record.id}">
<t t-if="! record.has_voted"><span>+1</span></t>
<t t-if="record.has_voted"><span>-1</span></t>
</button>

View File

@ -7,7 +7,6 @@
-->
<div t-name="mail.followers" class="oe_mail_recthread_aside oe_semantic_html_override">
<div class="oe_mail_recthread_actions">
<button type="button" class="oe_mail_button_invite">Invite</button>
<button type="button" class="oe_mail_button_follow">Follow</button>
<button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button>
</div>
@ -18,7 +17,7 @@
</div>
<div class="oe_mail_recthread_followers">
<t t-if="widget.options.title">
<h4><t t-raw="widget.options.title"/></h4>
<h4><t t-raw="widget.options.title"/></h4><span class="oe_mail_invite_wrapper"> · <a class="oe_mail_invite" >Invite partners</a></span>
</t>
<ul class="oe_mail_followers_display"></ul>
</div>

View File

@ -18,10 +18,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_mail
from . import test_mail, test_mail_access_rights
checks = [
test_mail,
test_mail_access_rights,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.addons.mail.tests import test_mail
from osv.orm import except_orm
class test_mail_access_rights(test_mail.TestMailMockups):
def setUp(self):
super(test_mail_access_rights, self).setUp()
cr, uid = self.cr, self.uid
self.mail_group = self.registry('mail.group')
self.mail_message = self.registry('mail.message')
self.mail_notification = self.registry('mail.notification')
self.res_users = self.registry('res.users')
self.res_groups = self.registry('res.groups')
self.res_partner = self.registry('res.partner')
# create a 'pigs' group that will be used through the various tests
self.group_pigs_id = self.mail_group.create(self.cr, self.uid,
{'name': 'Pigs', 'description': 'Fans of Pigs, unite !'})
# Find Employee group
group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
self.group_employee_id = group_employee_ref and group_employee_ref[1] or False
# Create Bert (without groups) and Raoul( employee)
self.user_bert_id = self.res_users.create(cr, uid, {'name': 'Bert Tartopoils', 'login': 'bert', 'groups_id': [(6, 0, [])]})
self.user_raoul_id = self.res_users.create(cr, uid, {'name': 'Raoul Grosbedon', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]})
self.user_bert = self.res_users.browse(cr, uid, self.user_bert_id)
self.partner_bert_id = self.user_bert.partner_id.id
self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
self.partner_raoul_id = self.user_raoul.partner_id.id
def test_00_mail_message_read_access_rights(self):
""" Test basic mail_message read access rights. """
cr, uid = self.cr, self.uid
partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
# Prepare groups: Pigs (employee), Jobs (public)
self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
self.group_jobs_id = self.mail_group.create(cr, uid, {'name': 'Jobs', 'public': 'public'})
# ----------------------------------------
# CASE1: Bert, basic mail.message read access
# ----------------------------------------
# Do: create a new mail.message
message_id = self.mail_message.create(cr, uid, {'body': 'My Body'})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
self.assertRaises(except_orm, self.mail_message.read,
cr, user_bert_id, message_id)
# Do: message is pushed to Bert
notif_id = self.mail_notification.create(cr, uid, {'message_id': message_id, 'partner_id': partner_bert_id})
# Test: Bert reads the message, ok because notification pushed
self.mail_message.read(cr, user_bert_id, message_id)
# Do: remove notification
self.mail_notification.unlink(cr, uid, notif_id)
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
self.assertRaises(except_orm, self.mail_message.read,
cr, self.user_bert_id, message_id)
# Do: Bert is now the author
self.mail_message.write(cr, uid, [message_id], {'author_id': partner_bert_id})
# Test: Bert reads the message, ok because Bert is the author
self.mail_message.read(cr, user_bert_id, message_id)
# Do: Bert is not the author anymore
self.mail_message.write(cr, uid, [message_id], {'author_id': partner_raoul_id})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
self.assertRaises(except_orm, self.mail_message.read,
cr, user_bert_id, message_id)
# Do: message is attached to a document Bert can read, Jobs
self.mail_message.write(cr, uid, [message_id], {'model': 'mail.group', 'res_id': self.group_jobs_id})
# Test: Bert reads the message, ok because linked to a doc he is allowed to read
self.mail_message.read(cr, user_bert_id, message_id)
# Do: message is attached to a document Bert cannot read, Pigs
self.mail_message.write(cr, uid, [message_id], {'model': 'mail.group', 'res_id': self.group_pigs_id})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
self.assertRaises(except_orm, self.mail_message.read,
cr, user_bert_id, message_id)
def test_05_mail_message_search_access_rights(self):
""" Test mail_message search override about access rights. """
self.assertTrue(1 == 1, 'Test not implemented, do not replace by return True')
def test_10_mail_flow_access_rights(self):
""" Test a Chatter-looks alike flow. """
cr, uid = self.cr, self.uid
partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
# Prepare groups: Pigs (employee), Jobs (public)
self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
self.group_jobs_id = self.mail_group.create(cr, uid, {'name': 'Jobs', 'public': 'public'})
# ----------------------------------------
# CASE1: Bert, without groups
# ----------------------------------------
# Do: Bert creates a group, should crash because perm_create only for employees
self.assertRaises(except_orm,
self.mail_group.create,
cr, user_bert_id, {'name': 'Bert\'s Group'})
# Do: Bert reads Jobs basic fields, ok because public = read access on the group
self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['name', 'description'])
# Do: Bert browse Pigs, ok (no direct browse of partners)
self.mail_group.browse(cr, user_bert_id, self.group_jobs_id)
# Do: Bert reads Jobs messages, ok because read access on the group => read access on its messages
jobs_message_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_ids'])['message_ids']
self.mail_message.read(cr, user_bert_id, jobs_message_ids)
# Do: Bert reads Jobs followers, ko because partner are accessible to employees or partner manager
jobs_followers_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_follower_ids'])['message_follower_ids']
self.assertRaises(except_orm,
self.res_partner.read,
cr, user_bert_id, jobs_followers_ids)
# Do: Bert comments Jobs, ko because no write access on the group and not in the followers
self.assertRaises(except_orm,
self.mail_group.message_post,
cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
# Do: add Bert to jobs followers
self.mail_group.message_subscribe(cr, uid, [self.group_jobs_id], [partner_bert_id])
# Do: Bert comments Jobs, ok because he is now in the followers
self.mail_group.message_post(cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
# Do: Bert reads Pigs, should crash because mail.group security=groups only for employee group
self.assertRaises(except_orm,
self.mail_group.read,
cr, user_bert_id, self.group_pigs_id)
# ----------------------------------------
# CASE1: Raoul, employee
# ----------------------------------------
# Do: Bert read Pigs, ok because public
self.mail_group.read(cr, user_raoul_id, self.group_pigs_id)
# Do: Bert read Jobs, ok because group_public_id = employee
self.mail_group.read(cr, user_raoul_id, self.group_jobs_id)

View File

@ -29,7 +29,13 @@ class res_partner(osv.osv):
'partner_id', 'Workitems',
readonly=True),
}
def copy(self, cr, uid, id, default={}, context=None):
default.update({
'workitem_ids': [],
})
return super(res_partner, self).copy(cr, uid, id, default=default, context=context)
res_partner()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -12,7 +12,7 @@
<attribute name="invisible">False</attribute>
</xpath>
<xpath expr="//page[@name='page_history']" position="inside">
<group name="grp_campaign" string="Campaign">
<group name="grp_campaign" string="Campaigns">
<field name="workitem_ids" colspan="4" nolabel="1"/>
</group>
</xpath>

View File

@ -338,7 +338,7 @@ class Partner(osv.osv):
-Paid Member: A member who has paid the membership amount."""),
'membership_start': fields.function(
_membership_date, multi = 'membeship_start',
string = 'Start Membership Date', type = 'date',
string = 'Membership Start Date', type = 'date',
store = {
'account.invoice': (_get_invoice_partner, ['state'], 10),
'membership.membership_line': (_get_partner_id, ['state'], 10, ),
@ -346,7 +346,7 @@ class Partner(osv.osv):
}, help="Date from which membership becomes active."),
'membership_stop': fields.function(
_membership_date,
string = 'Stop Membership Date', type='date', multi='membership_stop',
string = 'Membership End Date', type='date', multi='membership_stop',
store = {
'account.invoice': (_get_invoice_partner, ['state'], 10),
'membership.membership_line': (_get_partner_id, ['state'], 10),

View File

@ -106,6 +106,7 @@ class mrp_routing_workcenter(osv.osv):
"""
_name = 'mrp.routing.workcenter'
_description = 'Work Center Usage'
_order = 'sequence'
_columns = {
'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True),
'name': fields.char('Name', size=64, required=True),
@ -226,6 +227,7 @@ class mrp_bom(osv.osv):
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.bom', context=c),
}
_order = "sequence"
_parent_name = "bom_id"
_sql_constraints = [
('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0.\n' \
'You should install the mrp_subproduct module if you want to manage extra products on BoMs !'),
@ -671,7 +673,7 @@ class mrp_production(osv.osv):
for (production_id,name) in self.name_get(cr, uid, ids):
production = self.browse(cr, uid, production_id)
if production.move_prod_id:
if production.move_prod_id and production.move_prod_id.location_id.id != production.location_dest_id.id:
move_obj.write(cr, uid, [production.move_prod_id.id],
{'location_id': production.location_dest_id.id})
self.action_ready_send_note(cr, uid, [production_id], context)
@ -829,7 +831,8 @@ class mrp_production(osv.osv):
'ref': wc.code,
'product_id': wc.product_id.id,
'unit_amount': wc_line.hour,
'product_uom_id': wc.product_id.uom_id.id
'product_uom_id': wc.product_id.id and wc.product_id.uom_id.id or False
} )
if wc.costs_journal_id and wc.costs_general_account_id:
value = wc_line.cycle * wc.costs_cycle
@ -845,7 +848,8 @@ class mrp_production(osv.osv):
'ref': wc.code,
'product_id': wc.product_id.id,
'unit_amount': wc_line.cycle,
'product_uom_id': wc.product_id.uom_id.id
'product_uom_id': wc.product_id.id and wc.product_id.uom_id.id or False
} )
return amount

View File

@ -409,8 +409,9 @@
</page>
</notebook>
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
@ -544,9 +545,7 @@
<field name="name">Bill of Materials Structure</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.bom</field>
<field name="domain">[('id','in',active_ids)]</field>
<field name="view_type">tree</field>
<field name="view_mode">tree</field>
<field name="domain">[('bom_id', '=',active_ids)]</field>
<field name="view_id" ref="mrp_bom_tree_view"/>
</record>
<record id="ir_BOM_structure" model="ir.values">
@ -675,7 +674,7 @@
<field name="product_id"/>
<field name="product_qty" string="Quantity"/>
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
<field name="prodlot_id" groups="stock.group_production_lot"/>
<field name="prodlot_id" groups="stock.group_production_lot" context="{'product_id': product_id}"/>
<field name="state" invisible="1"/>
<button name="%(stock.move_consume)d"
string="Consume Products" type="action"
@ -799,6 +798,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -107,6 +107,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -189,6 +189,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>

View File

@ -117,8 +117,11 @@
<field name="stage_id" domain="[('user_id','=',uid)]" widget="statusbar" clickable="1"/>
</header>
<field name="memo" widget="html"/><!-- editor_width="100%%" editor_height="60%%" -->
<field name="message_follower_ids" widget="mail_followers"/>
<field class="oe_chatter" name="message_ids" widget="mail_thread"/>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
</record>

View File

@ -5,6 +5,7 @@
<!-- Mail group for the company's news -->
<record id="company_news_feed" model="mail.group">
<field name="name">Company's news</field>
<field name="public">public</field>
</record>
<record id="action_news" model="ir.actions.act_window">
@ -17,6 +18,7 @@
<!-- Mail group for the company's jobs -->
<record id="company_jobs" model="mail.group">
<field name="name">Company's jobs</field>
<field name="public">public</field>
</record>
<record id="action_jobs" model="ir.actions.act_window">

View File

@ -9,7 +9,9 @@
<field name="password">portal</field>
<!-- Avoid auto-including this user in any default group -->
<field name="groups_id" eval="[(5,)]"/>
<field name="partner_id" eval="ref('base.res_partner_1')"/>
<field name="supplier" eval="False"/>
<field name="customer" eval="True"/>
<field name="email">demo@portal.wrong.address</field>
</record>
<!-- Add the demo user to the portal (and therefore to the portal member group) -->

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