[MERGE] merged with trunk up to revision 9425. Needs to find another icon for Traceability on lot, though

bzr revid: qdp-launchpad@openerp.com-20140507182917-erjammra4okyei1c
This commit is contained in:
Quentin (OpenERP) 2014-05-07 20:29:17 +02:00
commit 70eb74a260
221 changed files with 2649 additions and 20756 deletions

View File

@ -409,7 +409,7 @@ class account_invoice(osv.osv):
'''
assert len(ids) == 1, 'This option should only be used for a single id at a time.'
self.write(cr, uid, ids, {'sent': True}, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_invoice', context=context)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_invoice', context=context)
def action_invoice_sent(self, cr, uid, ids, context=None):
'''
@ -1780,7 +1780,7 @@ class res_partner(osv.osv):
""" Inherits partner and adds invoice information in the partner form """
_inherit = 'res.partner'
_columns = {
'invoice_ids': fields.one2many('account.invoice.line', 'partner_id', 'Invoices', readonly=True),
'invoice_ids': fields.one2many('account.invoice', 'partner_id', 'Invoices', readonly=True),
}
def _find_accounting_partner(self, partner):

View File

@ -1040,7 +1040,7 @@ class account_move_line(osv.osv):
if opening_reconciliation:
obj_move_rec.write(cr, uid, unlink_ids, {'opening_reconciliation': False})
obj_move_rec.unlink(cr, uid, unlink_ids)
if all_moves:
if len(all_moves) >= 2:
obj_move_line.reconcile_partial(cr, uid, all_moves, 'auto',context=context)
return True

View File

@ -162,6 +162,26 @@ class res_partner(osv.osv):
def _debit_search(self, cr, uid, obj, name, args, context=None):
return self._asset_difference_search(cr, uid, obj, name, 'payable', args, context=context)
def _invoice_total(self, cr, uid, ids, field_name, arg, context=None):
result = {}
account_invoice_report = self.pool.get('account.invoice.report')
for partner in self.browse(cr, uid, ids, context=context):
invoice_ids = account_invoice_report.search(cr, uid, [('partner_id','child_of',partner.id)], context=context)
invoices = account_invoice_report.browse(cr, uid, invoice_ids, context=context)
result[partner.id] = sum(inv.user_currency_price_total for inv in invoices)
return result
def _journal_item_count(self, cr, uid, ids, field_name, arg, context=None):
MoveLine = self.pool('account.move.line')
AnalyticAccount = self.pool('account.analytic.account')
return {
partner_id: {
'journal_item_count': MoveLine.search_count(cr, uid, [('partner_id', '=', partner_id)], context=context),
'contracts_count': AnalyticAccount.search_count(cr,uid, [('partner_id', '=', partner_id)], context=context)
}
for partner_id in ids
}
def has_something_to_reconcile(self, cr, uid, partner_id, context=None):
'''
at least a debit, a credit and a line older than the last reconciliation date of the partner
@ -190,6 +210,9 @@ class res_partner(osv.osv):
fnct_search=_credit_search, string='Total Receivable', multi='dc', help="Total amount this customer owes you."),
'debit': fields.function(_credit_debit_get, fnct_search=_debit_search, string='Total Payable', multi='dc', help="Total amount you have to pay to this supplier."),
'debit_limit': fields.float('Payable Limit'),
'total_invoiced': fields.function(_invoice_total, string="Total Invoiced", type='float'),
'contracts_count': fields.function(_journal_item_count, string="Contracts", type='integer', multi="invoice_journal"),
'journal_item_count': fields.function(_journal_item_count, string="Journal Items", type="integer", multi="invoice_journal"),
'property_account_payable': fields.property(
type='many2one',
relation='account.account',

View File

@ -64,13 +64,21 @@
<field name="priority" eval="20"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button type="action" string="Invoices"
name="%(account.action_invoice_tree1)d"
<button type="action"
class="oe_stat_button"
icon="fa-pencil-square-o"
name="%(account.action_invoice_tree)d"
attrs="{'invisible': [('customer', '=', False)]}"
context="{'search_default_partner_id': active_id,'default_partner_id': active_id}" groups="account.group_account_invoice"/>
<button type="action" string="Journal Items" name="%(account.action_account_moves_all_tree)d" groups="account.group_account_user"/>
<button type="action" string="Contracts" name="%(account.action_open_partner_analytic_accounts)d"
groups="analytic.group_analytic_accounting"/>
context="{'search_default_partner_id': active_id,'default_partner_id': active_id}" groups="account.group_account_invoice">
<div><strong><field name="total_invoiced" widget="monetary"/></strong><br/>Invoiced</div>
</button>
<button type="action" class="oe_stat_button" name="%(account.action_account_moves_all_tree)d" groups="account.group_account_user" icon="fa-list">
<field string="Journal Items" name="journal_item_count" widget="statinfo"/>
</button>
<button class="oe_inline oe_stat_button" type="action" name="%(account.action_open_partner_analytic_accounts)d"
icon="fa-book" groups="analytic.group_analytic_accounting">
<field string="Contracts" name="contracts_count" widget="statinfo"/>
</button>
</xpath>
</field>
</record>

View File

@ -50,6 +50,6 @@ class account_analytic_balance(osv.osv_memory):
datas['form']['active_ids'] = context.get('active_ids', False)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_analyticbalance', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_analyticbalance', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -49,6 +49,6 @@ class account_analytic_cost_ledger_journal_report(osv.osv_memory):
}
datas['form']['active_ids'] = context.get('active_ids', False)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_analyticcostledgerquantity', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_analyticcostledgerquantity', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -49,6 +49,6 @@ class account_analytic_cost_ledger(osv.osv_memory):
datas['form']['active_ids'] = context.get('active_ids', False)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_analyticcostledger', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_analyticcostledger', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -47,6 +47,6 @@ class account_analytic_inverted_balance(osv.osv_memory):
'form': data
}
datas['form']['active_ids'] = context.get('active_ids', False)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_invertedanalyticbalance', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_invertedanalyticbalance', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -57,7 +57,7 @@ class account_analytic_journal_report(osv.osv_memory):
context2 = context.copy()
context2['active_model'] = 'account.analytic.journal'
context2['active_ids'] = ids_list
return self.pool['report'].get_action(cr, uid, ids, 'account.report_analyticjournal', data=datas, context=context2)
return self.pool['report'].get_action(cr, uid, [], 'account.report_analyticjournal', data=datas, context=context2)
def default_get(self, cr, uid, fields, context=None):
if context is None:

View File

@ -47,7 +47,6 @@
<tr>
<th>Description</th>
<th>Quantity</th>
<th groups="product.group_uom">Unit of measure</th>
<th class="text-right">Unit Price</th>
<th class="text-right" groups="sale.group_discount_per_so_line">Discount (%)</th>
<th class="text-right">Taxes</th>
@ -57,8 +56,10 @@
<tbody class="invoice_tbody">
<tr t-foreach="o.invoice_line" t-as="l">
<td><span t-field="l.name"/></td>
<td><span t-field="l.quantity"/></td>
<td groups="product.group_uom"><span t-field="l.uos_id"/></td>
<td>
<span t-field="l.quantity"/>
<span t-field="l.uos_id" groups="product.group_uom"/>
</td>
<td class="text-right">
<span t-field="l.price_unit"/>
</td>
@ -100,7 +101,7 @@
</div>
<div class="row" t-if="o.tax_line">
<div class="col-xs-3">
<div class="col-xs-6">
<table class="table table-condensed">
<thead>
<tr>
@ -136,7 +137,7 @@
</p>
<p t-if="o.fiscal_position">
<strong>Fiscal Position:</strong>
<span t-field="o.fiscal_position.note"/>
<span t-field="o.fiscal_position"/>
</p>
</div>
</t>

View File

@ -89,6 +89,6 @@ class accounting_report(osv.osv_memory):
def _print_report(self, cr, uid, ids, data, context=None):
data['form'].update(self.read(cr, uid, ids, ['date_from_cmp', 'debit_credit', 'date_to_cmp', 'fiscalyear_id_cmp', 'period_from_cmp', 'period_to_cmp', 'filter_cmp', 'account_report_id', 'enable_filter', 'label_filter','target_move'], context=context)[0])
return self.pool['report'].get_action(cr, uid, ids, 'account.report_financial', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_financial', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -21,6 +21,7 @@
from openerp.osv import fields, osv
class account_balance_report(osv.osv_memory):
_inherit = "account.common.account.report"
_name = 'account.balance.report'
@ -36,6 +37,6 @@ class account_balance_report(osv.osv_memory):
def _print_report(self, cr, uid, ids, data, context=None):
data = self.pre_print_report(cr, uid, ids, data, context=context)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_trialbalance', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_trialbalance', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -81,6 +81,6 @@ class account_aged_trial_balance(osv.osv_memory):
data['form'].update(res)
if data.get('form',False):
data['ids']=[data['form'].get('chart_account_id',False)]
return self.pool['report'].get_action(cr, uid, ids, 'account.report_agedpartnerbalance', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_agedpartnerbalance', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -21,6 +21,7 @@
from openerp.osv import fields, osv
class account_central_journal(osv.osv_memory):
_name = 'account.central.journal'
_description = 'Account Central Journal'
@ -32,6 +33,6 @@ class account_central_journal(osv.osv_memory):
def _print_report(self, cr, uid, ids, data, context=None):
data = self.pre_print_report(cr, uid, ids, data, context=context)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_centraljournal', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_centraljournal', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -21,6 +21,7 @@
from openerp.osv import fields, osv
class account_general_journal(osv.osv_memory):
_inherit = "account.common.journal.report"
_name = 'account.general.journal'
@ -32,6 +33,6 @@ class account_general_journal(osv.osv_memory):
def _print_report(self, cr, uid, ids, data, context=None):
data = self.pre_print_report(cr, uid, ids, data, context=context)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_generaljournal', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_generaljournal', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -59,6 +59,6 @@ class account_report_general_ledger(osv.osv_memory):
if data['form']['landscape'] is False:
data['form'].pop('landscape')
return self.pool['report'].get_action(cr, uid, ids, 'account.report_generalledger', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_generalledger', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -44,6 +44,6 @@ class account_partner_balance(osv.osv_memory):
context = {}
data = self.pre_print_report(cr, uid, ids, data, context=context)
data['form'].update(self.read(cr, uid, ids, ['display_partner'])[0])
return self.pool['report'].get_action(cr, uid, ids, 'account.report_partnerbalance', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_partnerbalance', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -58,7 +58,7 @@ class account_partner_ledger(osv.osv_memory):
data = self.pre_print_report(cr, uid, ids, data, context=context)
data['form'].update(self.read(cr, uid, ids, ['initial_balance', 'filter', 'page_split', 'amount_currency'])[0])
if data['form'].get('page_split') is True:
return self.pool['report'].get_action(cr, uid, ids, 'account.report_partnerledgerother', data=data, context=context)
return self.pool['report'].get_action(cr, uid, ids, 'account.report_partnerledger', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_partnerledgerother', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_partnerledger', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -67,8 +67,8 @@ class account_print_journal(osv.osv_memory):
data = self.pre_print_report(cr, uid, ids, data, context=context)
data['form'].update(self.read(cr, uid, ids, ['sort_selection'], context=context)[0])
if context.get('sale_purchase_only'):
return self.pool['report'].get_action(cr, uid, ids, 'account.report_salepurchasejournal', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_salepurchasejournal', data=data, context=context)
else:
return self.pool['report'].get_action(cr, uid, ids, 'account.report_journal', data=data, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_journal', data=data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -61,6 +61,6 @@ class account_vat_declaration(osv.osv_memory):
taxcode = taxcode_obj.browse(cr, uid, [taxcode_id], context=context)[0]
datas['form']['company_id'] = taxcode.company_id.id
return self.pool['report'].get_action(cr, uid, ids, 'account.report_vat', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account.report_vat', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -652,12 +652,10 @@ class account_analytic_account(osv.osv):
'nodestroy': True,
}
def _prepare_invoice(self, cr, uid, contract, context=None):
def _prepare_invoice_data(self, cr, uid, contract, context=None):
context = context or {}
inv_obj = self.pool.get('account.invoice')
journal_obj = self.pool.get('account.journal')
fpos_obj = self.pool.get('account.fiscal.position')
if not contract.partner_id:
raise osv.except_osv(_('No Customer Defined!'),_("You must first select a Customer for Contract %s!") % contract.name )
@ -678,33 +676,36 @@ class account_analytic_account(osv.osv):
elif contract.company_id:
currency_id = contract.company_id.currency_id.id
inv_data = {
'reference': contract.code or False,
invoice = {
'account_id': contract.partner_id.property_account_receivable.id,
'type': 'out_invoice',
'partner_id': contract.partner_id.id,
'currency_id': currency_id,
'journal_id': len(journal_ids) and journal_ids[0] or False,
'date_invoice': contract.recurring_next_date,
'origin': contract.name,
'origin': contract.code,
'fiscal_position': fpos and fpos.id,
'payment_term': partner_payment_term,
'company_id': contract.company_id.id or False,
}
invoice_id = inv_obj.create(cr, uid, inv_data, context=context)
return invoice
def _prepare_invoice_lines(self, cr, uid, contract, fiscal_position_id, context=None):
fpos_obj = self.pool.get('account.fiscal.position')
fiscal_position = fpos_obj.browse(cr, uid, fiscal_position_id, context=context)
invoice_lines = []
for line in contract.recurring_invoice_line_ids:
res = line.product_id
account_id = res.property_account_income.id
if not account_id:
account_id = res.categ_id.property_account_income_categ.id
account_id = fpos_obj.map_account(cr, uid, fpos, account_id)
account_id = fpos_obj.map_account(cr, uid, fiscal_position, account_id)
taxes = res.taxes_id or False
tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
tax_id = fpos_obj.map_tax(cr, uid, fiscal_position, taxes)
invoice_line_vals = {
invoice_lines.append((0, 0, {
'name': line.name,
'account_id': account_id,
'account_analytic_id': contract.id,
@ -712,13 +713,14 @@ class account_analytic_account(osv.osv):
'quantity': line.quantity,
'uos_id': line.uom_id.id or False,
'product_id': line.product_id.id or False,
'invoice_id' : invoice_id,
'invoice_line_tax_id': [(6, 0, tax_id)],
}
self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context)
}))
return invoice_lines
inv_obj.button_compute(cr, uid, [invoice_id], context=context)
return invoice_id
def _prepare_invoice(self, cr, uid, contract, context=None):
invoice = self._prepare_invoice_data(cr, uid, contract, context=context)
invoice['invoice_line'] = self._prepare_invoice_lines(cr, uid, contract, invoice['fiscal_position'], context=context)
return invoice
def recurring_create_invoice(self, cr, uid, ids, context=None):
return self._recurring_create_invoice(cr, uid, ids, context=context)
@ -728,6 +730,7 @@ class account_analytic_account(osv.osv):
def _recurring_create_invoice(self, cr, uid, ids, automatic=False, context=None):
context = context or {}
invoice_ids = []
current_date = time.strftime('%Y-%m-%d')
if ids:
contract_ids = ids
@ -735,8 +738,8 @@ class account_analytic_account(osv.osv):
contract_ids = self.search(cr, uid, [('recurring_next_date','<=', current_date), ('state','=', 'open'), ('recurring_invoices','=', True), ('type', '=', 'contract')])
for contract in self.browse(cr, uid, contract_ids, context=context):
try:
invoice_id = self._prepare_invoice(cr, uid, contract, context=context)
invoice_values = self._prepare_invoice(cr, uid, contract, context=context)
invoice_ids.append(self.pool['account.invoice'].create(cr, uid, invoice_values, context=context))
next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d")
interval = contract.recurring_interval
if contract.recurring_rule_type == 'daily':
@ -754,7 +757,7 @@ class account_analytic_account(osv.osv):
_logger.error(traceback.format_exc())
else:
raise
return True
return invoice_ids
class account_analytic_account_summary_user(osv.osv):
_name = "account_analytic_analysis.summary.user"

View File

@ -114,6 +114,16 @@ class sale_order_line(osv.osv):
if rec:
inv_line_obj.write(cr, uid, [line.id], {'account_analytic_id': rec.analytic_id.id}, context=context)
return create_ids
class product_product(osv.Model):
_inherit = 'product.product'
def _rules_count(self, cr, uid, ids, field_name, arg, context=None):
Analytic = self.pool['account.analytic.default']
return {
product_id: Analytic.search_count(cr, uid, [('product_id', '=', product_id)], context=context)
for product_id in ids
}
_columns = {
'rules_count': fields.function(_rules_count, string='# Analytic Rules', type='integer'),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -80,7 +80,10 @@
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button string="Analytic Rules" name= "%(action_product_default_list)d" type="action" groups="analytic.group_analytic_accounting" />
<button class="oe_inline oe_stat_button" name= "%(action_product_default_list)d" type="action"
groups="analytic.group_analytic_accounting" icon="fa-xing">
<field string="Analytic Rules" name="rules_count" widget="statinfo" />
</button>
</xpath>
</field>
</record>

View File

@ -24,6 +24,7 @@ import time
from openerp.osv import fields, osv
from openerp.tools.translate import _
class account_crossovered_analytic(osv.osv_memory):
_name = "account.crossovered.analytic"
_description = "Print Crossovered Analytic"
@ -65,6 +66,6 @@ class account_crossovered_analytic(osv.osv_memory):
'model': 'account.analytic.account',
'form': data
}
return self.pool['report'].get_action(cr, uid, ids, 'account_analytic_plans.report_crossoveredanalyticplans', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account_analytic_plans.report_crossoveredanalyticplans', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -237,9 +237,15 @@ class account_asset_asset(osv.osv):
if salvage_value:
val['value_residual'] = purchase_value - salvage_value
return {'value': val}
def _entry_count(self, cr, uid, ids, field_name, arg, context=None):
MoveLine = self.pool('account.move.line')
return {
asset_id: MoveLine.search_count(cr, uid, [('asset_id', '=', asset_id)], context=context)
for asset_id in ids
}
_columns = {
'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
'entry_count': fields.function(_entry_count, string='# Asset Entries', type='integer'),
'name': fields.char('Asset Name', size=64, required=True, readonly=True, states={'draft':[('readonly',False)]}),
'code': fields.char('Reference', size=32, readonly=True, states={'draft':[('readonly',False)]}),
'purchase_value': fields.float('Gross Value', required=True, readonly=True, states={'draft':[('readonly',False)]}),

View File

@ -84,7 +84,9 @@
</header>
<sheet>
<div class="oe_button_box oe_right">
<button name="open_entries" string="Entries" type="object" class="oe_inline"/>
<button class="oe_inline oe_stat_button" name="open_entries" type="object" icon="fa-pencil">
<field string="Entries" name="entry_count" widget="statinfo" />
</button>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>

View File

@ -46,6 +46,6 @@ class account_budget_analytic(osv.osv_memory):
'form': data
}
datas['form']['ids'] = datas['ids']
return self.pool['report'].get_action(cr, uid, ids, 'account_budget.report_analyticaccountbudget', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account_budget.report_analyticaccountbudget', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -47,6 +47,6 @@ class account_budget_crossvered_report(osv.osv_memory):
}
datas['form']['ids'] = datas['ids']
datas['form']['report'] = 'analytic-full'
return self.pool['report'].get_action(cr, uid, ids, 'account_budget.report_crossoveredbudget', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account_budget.report_crossoveredbudget', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -49,6 +49,6 @@ class account_budget_crossvered_summary_report(osv.osv_memory):
}
datas['form']['ids'] = datas['ids']
datas['form']['report'] = 'analytic-one'
return self.pool['report'].get_action(cr, uid, ids, 'account_budget.report_crossoveredbudget', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account_budget.report_crossoveredbudget', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -47,6 +47,6 @@ class account_budget_report(osv.osv_memory):
}
datas['form']['ids'] = datas['ids']
datas['form']['report'] = 'analytic-full'
return self.pool['report'].get_action(cr, uid, ids, 'account_budget.report_budget', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account_budget.report_budget', data=datas, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -74,26 +74,17 @@ class account_voucher(osv.osv):
def print_check(self, cr, uid, ids, context=None):
if not ids:
return {}
raise osv.except_osv(_('Printing error'), _('No check selected '))
check_layout_report = {
'top' : 'account.print.check.top',
'middle' : 'account.print.check.middle',
'bottom' : 'account.print.check.bottom',
data = {
'id': ids and ids[0],
'ids': ids,
}
check_layout = self.browse(cr, uid, ids[0], context=context).company_id.check_layout
return {
'type': 'ir.actions.report.xml',
'report_name':check_layout_report[check_layout],
'datas': {
'model':'account.voucher',
'id': ids and ids[0] or False,
'ids': ids and ids or [],
'report_type': 'pdf'
},
'nodestroy': True
}
return self.pool['report'].get_action(
cr, uid, [], 'account_check_writing.report_check', data=data, context=context
)
def create(self, cr, uid, vals, context=None):
if vals.get('amount') and vals.get('journal_id') and 'amount_in_word' not in vals:
vals['amount_in_word'] = self._amount_to_text(cr, uid, vals['amount'], vals.get('currency_id') or \

View File

@ -20,9 +20,9 @@
##############################################################################
from openerp.tools.translate import _
from openerp.osv import fields, osv
class account_check_write(osv.osv_memory):
_name = 'account.check.write'
_description = 'Prin Check in Batch'
@ -64,23 +64,11 @@ class account_check_write(osv.osv_memory):
ir_sequence_obj.write(cr, uid, sequence_id, {'number_next': new_value})
#print the checks
check_layout_report = {
'top' : 'account.print.check.top',
'middle' : 'account.print.check.middle',
'bottom' : 'account.print.check.bottom',
data = {
'id': voucher_ids and voucher_ids[0],
'ids': voucher_ids,
}
check_layout = voucher_obj.browse(cr, uid, voucher_ids[0], context=context).company_id.check_layout
if not check_layout:
check_layout = 'top'
return {
'type': 'ir.actions.report.xml',
'report_name':check_layout_report[check_layout],
'datas': {
'model':'account.voucher',
'ids': voucher_ids,
'report_type': 'pdf'
},
'nodestroy': True
}
return self.pool['report'].get_action(
cr, uid, [], 'account_check_writing.report_check', data=data, context=context
)

View File

@ -188,7 +188,7 @@ class res_partner(osv.osv):
'model': 'account_followup.followup',
'form': data
}
return self.pool['report'].get_action(cr, uid, wizard_partner_ids, 'account_followup.report_followup', data=datas, context=context)
return self.pool['report'].get_action(cr, uid, [], 'account_followup.report_followup', data=datas, context=context)
def do_partner_mail(self, cr, uid, partner_ids, context=None):
if context is None:

View File

@ -69,14 +69,14 @@
<field name="state" widget="statusbar" statusbar_visible="draft,open"/>
</header>
<sheet string="Payment order">
<div class="oe_button_box">
<button name="%(action_create_payment_order)d" string="Select Invoices to Pay"
type="action" attrs="{'invisible':[('state','=','done')]}" icon="gtk-find"/>
</div>
<div class="oe_title">
<label for="reference" class="oe_edit_only"/>
<h1><field name="reference"/></h1>
</div>
<div class=" oe_right oe_button_box">
<button class="oe_inline oe_stat_button oe_right" name="%(action_create_payment_order)d" string="Invoices" Help="Select Invoices to Pay"
type="action" attrs="{'invisible':[('state','=','done')]}" icon="fa-pencil-square-o" widget="statinfo"/>
</div>
<group>
<group>
<field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'account.group_account_invoice']}"/>
@ -292,7 +292,7 @@
<field name="inherit_id" ref="account.view_bank_statement_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='import_buttons']" position="inside">
<button name="%(action_account_populate_statement_confirm)d" attrs="{'invisible':[('state','=','confirm')]}" string="Import Payment Lines" type="action" icon="gtk-execute"/>
<button class="oe_inline oe_stat_button" name="%(action_account_populate_statement_confirm)d" attrs="{'invisible':[('state','=','confirm')]}" string="Import Lines" help="Import Payment Lines" type="action" icon="fa-cogs" widget="statinfo"/>
</xpath>
</field>
</record>

View File

@ -200,9 +200,9 @@
<field name="inherit_id" ref="account.view_bank_statement_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='import_buttons']" position="inside">
<button name="%(action_view_account_statement_from_invoice_lines)d"
string="Import Invoices" type="action"
attrs="{'invisible':[('state','=','confirm')]}"/>
<button class="oe_inline oe_stat_button" name="%(action_view_account_statement_from_invoice_lines)d"
string="Import Invoice" type="action"
attrs="{'invisible':[('state','=','confirm')]}" widget="statinfo" icon="fa-pencil-square-o"/>
</xpath>
</field>
</record>

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2012-12-12 16:27+0000\n"
"Last-Translator: Rui Franco (multibase.pt) <Unknown>\n"
"PO-Revision-Date: 2014-05-06 17:17+0000\n"
"Last-Translator: Daniel Reis <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:15+0000\n"
"X-Generator: Launchpad (build 16985)\n"
"X-Launchpad-Export-Date: 2014-05-07 06:26+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: analytic_user_function
#: model:ir.model,name:analytic_user_function.model_account_analytic_line
@ -24,7 +24,7 @@ msgstr "Linha analítica"
#. module: analytic_user_function
#: view:account.analytic.account:0
msgid "Invoice Price Rate per User"
msgstr ""
msgstr "Tarifa faturável por cada Utilizador"
#. module: analytic_user_function
#: field:analytic.user.funct.grid,product_id:0
@ -44,7 +44,7 @@ msgstr "Preço"
#. module: analytic_user_function
#: help:analytic.user.funct.grid,price:0
msgid "Price per hour for this user."
msgstr "Preço à hora para este utilizador."
msgstr "Preço hora para este utilizador."
#. module: analytic_user_function
#: field:analytic.user.funct.grid,account_id:0
@ -67,7 +67,7 @@ msgstr "Dados de faturação"
#. module: analytic_user_function
#: field:account.analytic.account,user_product_ids:0
msgid "Users/Products Rel."
msgstr "Utilizadores/Relação de Artigos"
msgstr "Realação Utilizadores/Produtos"
#. module: analytic_user_function
#: view:account.analytic.account:0
@ -78,6 +78,11 @@ msgid ""
" of the default values when invoicing the "
"customer."
msgstr ""
"Definir um serviço específico (ex: Consultor Sénior)\n"
" e preço respetivo para alguns utilizadore, para "
"que seja usado em vez\n"
" dos valores predefinidos, no momento de faturar "
"o cliente."
#. module: analytic_user_function
#: field:analytic.user.funct.grid,uom_id:0
@ -89,8 +94,7 @@ msgstr "Unidade de medida"
#: code:addons/analytic_user_function/analytic_user_function.py:136
#, python-format
msgid "There is no expense account define for this product: \"%s\" (id:%d)"
msgstr ""
"Não há qualquer conta de despesas definida para o artigo: \"%s\" (id:%d)"
msgstr "Não há conta de despesas definida para o artigo: \"%s\" (id:%d)"
#. module: analytic_user_function
#: model:ir.model,name:analytic_user_function.model_hr_analytic_timesheet
@ -106,6 +110,12 @@ msgid ""
" specific user. This allows to set invoicing\n"
" conditions for a group of contracts."
msgstr ""
"O OpenERP irá procurar recursivamente nas contas ascendentes\n"
" para verificar se existem condições específicas "
"para\n"
" certos utilizadores. Isso permite definir "
"condições\n"
" de faturação para grupos de contratos."
#. module: analytic_user_function
#: field:analytic.user.funct.grid,user_id:0

View File

@ -11,6 +11,7 @@
<field name="name" />
<field name="client_id" />
<field name="enabled" />
<field name="body" />
</group>
<group>
<field name="auth_endpoint" />

View File

@ -41,5 +41,11 @@
</xpath>
</template>
<template id="assets" inherit_id="web.assets_backend" name="base module import test">
<xpath expr="." position="inside">
<script type="text/javascript" src="/test_module/static/src/js/test.js"></script>
</xpath>
</template>
</data>
</openerp>

View File

@ -22,7 +22,7 @@
{
'name': 'Calendar',
'version': '1.0',
'depends': ['base', 'mail', 'base_action_rule','web_calendar'],
'depends': ['base', 'mail', 'base_action_rule', 'web_calendar'],
'summary': 'Personal & Shared Calendar',
'description': """
This is a full-featured calendar system.
@ -41,20 +41,19 @@ If you need to manage your meetings, you should install the CRM module.
'demo': ['calendar_demo.xml'],
'data': [
'security/ir.model.access.csv',
'security/calendar_security.xml',
'calendar_view.xml',
'contacts_view.xml',
'calendar_data.xml',
'views/calendar.xml',
],
'qweb': ['static/src/xml/*.xml'],
'test' : [
'test/calendar_test.yml',
'test/test_calendar_recurrent_event_case2.yml'
],
'test': [
'test/calendar_test.yml',
'test/test_calendar_recurrent_event_case2.yml'
],
'installable': True,
'application': True,
'auto_install': False,
'images': ['images/calendar1.jpeg','images/calendar2.jpeg','images/calendar3.jpeg','images/calendar4.jpeg'],
'images': ['images/calendar1.jpeg', 'images/calendar2.jpeg', 'images/calendar3.jpeg', 'images/calendar4.jpeg'],
}

View File

@ -54,10 +54,10 @@ def calendar_id2real_id(calendar_id=None, with_date=False):
if len(res) >= 2:
real_id = res[0]
if with_date:
real_date = time.strftime("%Y-%m-%d %H:%M:%S", time.strptime(res[1], "%Y%m%d%H%M%S"))
start = datetime.strptime(real_date, "%Y-%m-%d %H:%M:%S")
real_date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT, time.strptime(res[1], "%Y%m%d%H%M%S"))
start = datetime.strptime(real_date, DEFAULT_SERVER_DATETIME_FORMAT)
end = start + timedelta(hours=with_date)
return (int(real_id), real_date, end.strftime("%Y-%m-%d %H:%M:%S"))
return (int(real_id), real_date, end.strftime(DEFAULT_SERVER_DATETIME_FORMAT))
return int(real_id)
return calendar_id and int(calendar_id) or calendar_id
@ -95,10 +95,6 @@ class calendar_attendee(osv.Model):
result[id][name] = attdata.partner_id.name or False
else:
result[id][name] = attdata.email or ''
if name == 'event_date':
result[id][name] = attdata.event_id.date
if name == 'event_end_date':
result[id][name] = attdata.event_id.date_deadline
return result
STATE_SELECTION = [
@ -113,8 +109,6 @@ class calendar_attendee(osv.Model):
'cn': fields.function(_compute_data, string='Common name', type="char", multi='cn', store=True),
'partner_id': fields.many2one('res.partner', 'Contact', readonly="True"),
'email': fields.char('Email', help="Email of Invited Person"),
'event_date': fields.function(_compute_data, string='Event Date', type="datetime", multi='event_date'),
'event_end_date': fields.function(_compute_data, string='Event End Date', type="datetime", multi='event_end_date'),
'availability': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly="True"),
'access_token': fields.char('Invitation Token'),
'event_id': fields.many2one('calendar.event', 'Meeting linked'),
@ -144,9 +138,12 @@ class calendar_attendee(osv.Model):
"""
res = None
def ics_datetime(idate, short=False):
def ics_datetime(idate, allday=False):
if idate:
return datetime.strptime(idate.split('.')[0], '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.timezone('UTC'))
if allday:
return datetime.strptime(idate.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT).replace(tzinfo=pytz.timezone('UTC'))
else:
return datetime.strptime(idate.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT).replace(tzinfo=pytz.timezone('UTC'))
return False
try:
@ -157,11 +154,11 @@ class calendar_attendee(osv.Model):
cal = vobject.iCalendar()
event = cal.add('vevent')
if not event_obj.date_deadline or not event_obj.date:
if not event_obj.start or not event_obj.stop:
raise osv.except_osv(_('Warning!'), _("First you have to specify the date of the invitation."))
event.add('created').value = ics_datetime(time.strftime('%Y-%m-%d %H:%M:%S'))
event.add('dtstart').value = ics_datetime(event_obj.date)
event.add('dtend').value = ics_datetime(event_obj.date_deadline)
event.add('created').value = ics_datetime(time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))
event.add('dtstart').value = ics_datetime(event_obj.start, event_obj.allday)
event.add('dtend').value = ics_datetime(event_obj.stop, event_obj.allday)
event.add('summary').value = event_obj.name
if event_obj.description:
event.add('description').value = event_obj.description
@ -198,7 +195,7 @@ class calendar_attendee(osv.Model):
"""
res = False
if self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.block_mail', default=False):
if self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.block_mail', default=False) or context.get("no_mail_to_attendees"):
return res
mail_ids = []
@ -312,6 +309,10 @@ class res_partner(osv.Model):
}
def get_attendee_detail(self, cr, uid, ids, meeting_id, context=None):
"""
Return a list of tuple (id, name, status)
Used by web_calendar.js : Many2ManyAttendee
"""
datas = []
meeting = False
if meeting_id:
@ -339,15 +340,15 @@ class calendar_alarm_manager(osv.AbstractModel):
base_request = """
SELECT
cal.id,
cal.date - interval '1' minute * calcul_delta.max_delta AS first_alarm,
cal.start - interval '1' minute * calcul_delta.max_delta AS first_alarm,
CASE
WHEN cal.recurrency THEN cal.end_date - interval '1' minute * calcul_delta.min_delta
ELSE cal.date_deadline - interval '1' minute * calcul_delta.min_delta
WHEN cal.recurrency THEN cal.final_date - interval '1' minute * calcul_delta.min_delta
ELSE cal.stop - interval '1' minute * calcul_delta.min_delta
END as last_alarm,
cal.date as first_event_date,
cal.start as first_event_date,
CASE
WHEN cal.recurrency THEN cal.end_date
ELSE cal.date_deadline
WHEN cal.recurrency THEN cal.final_date
ELSE cal.stop
END as last_event_date,
calcul_delta.min_delta,
calcul_delta.max_delta,
@ -380,7 +381,7 @@ class calendar_alarm_manager(osv.AbstractModel):
tuple_params = (type_to_read,)
#ADD FILTER ON PARTNER_ID
# ADD FILTER ON PARTNER_ID
if partner_id:
base_request += filter_user
tuple_params += (partner_id, )
@ -389,7 +390,7 @@ class calendar_alarm_manager(osv.AbstractModel):
tuple_params += (seconds, seconds,)
cr.execute("""SELECT *
FROM ( %s ) AS ALL_EVENTS
FROM ( %s WHERE cal.active = True ) AS ALL_EVENTS
WHERE ALL_EVENTS.first_alarm < (now() at time zone 'utc' + interval '%%s' second )
AND ALL_EVENTS.last_alarm > (now() at time zone 'utc' - interval '%%s' second )
""" % base_request, tuple_params)
@ -421,7 +422,7 @@ class calendar_alarm_manager(osv.AbstractModel):
for alarm in event.alarm_ids:
if alarm.type in alarm_type and \
one_date - timedelta(minutes=alarm.duration_minutes) < datetime.now() + timedelta(seconds=in_the_next_X_seconds) and \
(not after or one_date - timedelta(minutes=alarm.duration_minutes) > datetime.strptime(after.split('.')[0], "%Y-%m-%d %H:%M:%S")):
(not after or one_date - timedelta(minutes=alarm.duration_minutes) > datetime.strptime(after.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT)):
alert = {
'alarm_id': alarm.id,
'event_id': event.id,
@ -471,7 +472,7 @@ class calendar_alarm_manager(osv.AbstractModel):
if bFound and not LastFound: # if the precedent event had an alarm but not this one, we can stop the search for this event
break
else:
in_date_format = datetime.strptime(curEvent.date, '%Y-%m-%d %H:%M:%S')
in_date_format = datetime.strptime(curEvent.start, DEFAULT_SERVER_DATETIME_FORMAT)
LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, cron_interval, notif=False, context=context)
if LastFound:
for alert in LastFound:
@ -504,7 +505,7 @@ class calendar_alarm_manager(osv.AbstractModel):
if bFound and not LastFound: # if the precedent event had alarm but not this one, we can stop the search fot this event
break
else:
in_date_format = datetime.strptime(curEvent.date, '%Y-%m-%d %H:%M:%S')
in_date_format = datetime.strptime(curEvent.start, DEFAULT_SERVER_DATETIME_FORMAT)
LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, ajax_check_every_seconds, partner.calendar_last_notif_ack, mail=False, context=context)
if LastFound:
for alert in LastFound:
@ -539,7 +540,7 @@ class calendar_alarm_manager(osv.AbstractModel):
'title': event.name,
'message': message,
'timer': delta,
'notify_at': alert['notify_at'].strftime("%Y-%m-%d %H:%M:%S"),
'notify_at': alert['notify_at'].strftime(DEFAULT_SERVER_DATETIME_FORMAT),
}
@ -561,7 +562,7 @@ class calendar_alarm(osv.Model):
return res
_columns = {
'name': fields.char('Name', required=True), # fields function
'name': fields.char('Name', required=True),
'type': fields.selection([('notification', 'Notification'), ('email', 'Email')], 'Type', required=True),
'duration': fields.integer('Amount', required=True),
'interval': fields.selection([('minutes', 'Minutes'), ('hours', 'Hours'), ('days', 'Days')], 'Unit', required=True),
@ -579,7 +580,6 @@ class ir_values(osv.Model):
_inherit = 'ir.values'
def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False):
new_model = []
for data in models:
if type(data) in (list, tuple):
@ -648,7 +648,7 @@ class calendar_event_type(osv.Model):
class calendar_event(osv.Model):
""" Model for Calendar Event """
_name = 'calendar.event'
_description = "Meeting"
_description = "Event"
_order = "id desc"
_inherit = ["mail.thread", "ir.needaction_mixin"]
@ -658,7 +658,6 @@ class calendar_event(osv.Model):
def get_recurrent_date_by_event(self, cr, uid, event, context=None):
"""Get recurrent dates based on Rule string and all event where recurrent_id is child
"""
def todate(date):
val = parser.parse(''.join((re.compile('\d')).findall(date)))
## Dates are localized to saved timezone if any, else current timezone.
@ -666,8 +665,8 @@ class calendar_event(osv.Model):
val = pytz.UTC.localize(val)
return val.astimezone(timezone)
timezone = pytz.timezone(event.vtimezone or context.get('tz') or 'UTC')
startdate = pytz.UTC.localize(datetime.strptime(event.date, "%Y-%m-%d %H:%M:%S")) # Add "+hh:mm" timezone
timezone = pytz.timezone(context.get('tz') or 'UTC')
startdate = pytz.UTC.localize(datetime.strptime(event.start, DEFAULT_SERVER_DATETIME_FORMAT)) # Add "+hh:mm" timezone
if not startdate:
startdate = datetime.now()
@ -677,20 +676,19 @@ class calendar_event(osv.Model):
rset1 = rrule.rrulestr(str(event.rrule), dtstart=startdate, forceset=True)
ids_depending = self.search(cr, uid, [('recurrent_id', '=', event.id), '|', ('active', '=', False), ('active', '=', True)], context=context)
all_events = self.browse(cr, uid, ids_depending, context=context)
for ev in all_events:
rset1._exdate.append(todate(ev.recurrent_id_date))
return [d.astimezone(pytz.UTC) for d in rset1]
def _get_recurrency_end_date(self, data, context=None):
def _get_recurrency_end_date(self, cr, uid, id, context=None):
data = self.read(cr, uid, id, ['final_date', 'recurrency', 'rrule_type', 'count', 'end_type', 'stop'], context=context)
if not data.get('recurrency'):
return False
end_type = data.get('end_type')
end_date = data.get('end_date')
if end_type == 'count' and all(data.get(key) for key in ['count', 'rrule_type', 'date_deadline']):
final_date = data.get('final_date')
if end_type == 'count' and all(data.get(key) for key in ['count', 'rrule_type', 'stop']):
count = data['count'] + 1
delay, mult = {
'daily': ('days', 1),
@ -699,9 +697,9 @@ class calendar_event(osv.Model):
'yearly': ('years', 1),
}[data['rrule_type']]
deadline = datetime.strptime(data['date_deadline'], tools.DEFAULT_SERVER_DATETIME_FORMAT)
deadline = datetime.strptime(data['stop'], tools.DEFAULT_SERVER_DATETIME_FORMAT)
return deadline + relativedelta(**{delay: count * mult})
return end_date
return final_date
def _find_my_attendee(self, cr, uid, meeting_ids, context=None):
"""
@ -714,7 +712,25 @@ class calendar_event(osv.Model):
return attendee
return False
def _get_display_time(self, cr, uid, meeting_id, context=None):
def get_date_formats(self, cr, uid, context):
lang = context.get("lang")
res_lang = self.pool.get('res.lang')
lang_params = {}
if lang:
ids = res_lang.search(request.cr, uid, [("code", "=", lang)])
if ids:
lang_params = res_lang.read(request.cr, uid, ids[0], ["date_format", "time_format"])
format_date = lang_params.get("date_format", '%B-%d-%Y')
format_time = lang_params.get("time_format", '%I-%M %p')
return (format_date, format_time)
def get_display_time_tz(self, cr, uid, ids, tz=False, context=None):
if tz:
context["tz"] = tz
ev = self.browse(cr, uid, ids, context=context)[0]
return self._get_display_time(cr, uid, ev.start, ev.stop, ev.duration, ev.allday, context=context)
def _get_display_time(self, cr, uid, start, stop, zduration, zallday, context=None):
"""
Return date and time (from to from) based on duration with timezone in string :
eg.
@ -726,20 +742,22 @@ class calendar_event(osv.Model):
tz = context.get('tz', False)
if not tz: # tz can have a value False, so dont do it in the default value of get !
tz = pytz.timezone('UTC')
context['tz'] = self.pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['tz'])['tz']
tz = context['tz']
meeting = self.browse(cr, uid, meeting_id, context=context)
date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(meeting.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context)
date_deadline = fields.datetime.context_timestamp(cr, uid, datetime.strptime(meeting.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context)
event_date = date.strftime('%B-%d-%Y')
display_time = date.strftime('%H-%M')
if meeting.allday:
format_date, format_time = self.get_date_formats(cr, uid, context=context)
date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(start, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context)
date_deadline = fields.datetime.context_timestamp(cr, uid, datetime.strptime(stop, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context)
event_date = date.strftime(format_date)
display_time = date.strftime(format_time)
if zallday:
time = _("AllDay , %s") % (event_date)
elif meeting.duration < 24:
duration = date + timedelta(hours=meeting.duration)
time = ("%s at (%s To %s) (%s)") % (event_date, display_time, duration.strftime('%H-%M'), tz)
elif zduration < 24:
duration = date + timedelta(hours=zduration)
time = ("%s at (%s To %s) (%s)") % (event_date, display_time, duration.strftime(format_time), tz)
else:
time = ("%s at %s To\n %s at %s (%s)") % (event_date, display_time, date_deadline.strftime('%B-%d-%Y'), date_deadline.strftime('%H-%M'), tz)
time = ("%s at %s To\n %s at %s (%s)") % (event_date, display_time, date_deadline.strftime(format_date), date_deadline.strftime(format_time), tz)
return time
def _compute(self, cr, uid, ids, fields, arg, context=None):
@ -747,13 +765,21 @@ class calendar_event(osv.Model):
for meeting_id in ids:
res[meeting_id] = {}
attendee = self._find_my_attendee(cr, uid, [meeting_id], context)
meeting = self.browse(cr, uid, [meeting_id], context=context)[0]
for field in fields:
if field == 'is_attendee':
res[meeting_id][field] = True if attendee else False
elif field == 'attendee_status':
res[meeting_id][field] = attendee.state if attendee else 'needsAction'
elif field == 'display_time':
res[meeting_id][field] = self._get_display_time(cr, uid, meeting_id, context=context)
res[meeting_id][field] = self._get_display_time(cr, uid, meeting.start, meeting.stop, meeting.duration, meeting.allday, context=context)
elif field == "display_start":
res[meeting_id][field] = meeting.start_date if meeting.allday else meeting.start_datetime
elif field == 'start':
res[meeting_id][field] = meeting.start_date if meeting.allday else meeting.start_datetime
elif field == 'stop':
res[meeting_id][field] = meeting.stop_date if meeting.allday else meeting.stop_datetime
return res
def _get_rulestring(self, cr, uid, ids, name, arg, context=None):
@ -774,7 +800,7 @@ class calendar_event(osv.Model):
if data.count and data.count <= 0:
raise osv.except_osv(_('Warning!'), _('Count cannot be negative or 0.'))
data = self.read(cr, uid, id, ['id', 'byday', 'recurrency', 'month_list', 'end_date', 'rrule_type', 'month_by', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'week_list'], context=context)
data = self.read(cr, uid, id, ['id', 'byday', 'recurrency', 'month_list', 'final_date', 'rrule_type', 'month_by', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'week_list'], context=context)
event = data['id']
if data['recurrency']:
result[event] = self.compute_rule_string(data)
@ -793,20 +819,53 @@ class calendar_event(osv.Model):
if field_value:
data['recurrency'] = True
for event in self.browse(cr, uid, ids, context=context):
rdate = event.date
rdate = event.start
update_data = self._parse_rrule(field_value, dict(data), rdate)
data.update(update_data)
self.write(cr, uid, ids, data, context=context)
return True
def _tz_get(self, cr, uid, context=None):
return [(x.lower(), x) for x in pytz.all_timezones]
def _set_date(self, cr, uid, values, id=False, context=None):
if context is None:
context = {}
if values.get('start_datetime') or values.get('start_date') or values.get('start') \
or values.get('stop_datetime') or values.get('stop_date') or values.get('stop'):
allday = values.get("allday", None)
if allday is None:
if id:
allday = self.read(cr, uid, [id], ['allday'], context=context)[0].get('allday')
else:
allday = False
_logger.warning("Calendar - All day is not specified, arbitrarily set to False")
#raise osv.except_osv(_('Error!'), ("Need to know if it's an allday or not..."))
key = "date" if allday else "datetime"
notkey = "datetime" if allday else "date"
for fld in ('start', 'stop'):
if values.get('%s_%s' % (fld, key)) or values.get(fld):
values['%s_%s' % (fld, key)] = values.get('%s_%s' % (fld, key)) or values.get(fld)
values['%s_%s' % (fld, notkey)] = None
if fld not in values.keys():
values[fld] = values['%s_%s' % (fld, key)]
diff = False
if allday and values.get('stop_date') and values.get('start_date'):
diff = datetime.strptime(values['stop_date'].split(' ')[0], DEFAULT_SERVER_DATE_FORMAT) - datetime.strptime(values['start_date'].split(' ')[0], DEFAULT_SERVER_DATE_FORMAT)
elif values.get('stop_datetime') and values.get('start_datetime'):
diff = datetime.strptime(values['stop_datetime'].split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT) - datetime.strptime(values['start_datetime'].split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT)
if diff:
duration = float(diff.days) * 24 + (float(diff.seconds) / 3600)
values['duration'] = round(duration, 2)
_track = {
'location': {
'calendar.subtype_invitation': lambda self, cr, uid, obj, ctx=None: True,
},
'date': {
'start': {
'calendar.subtype_invitation': lambda self, cr, uid, obj, ctx=None: True,
},
}
@ -817,19 +876,26 @@ class calendar_event(osv.Model):
'is_attendee': fields.function(_compute, string='Attendee', type="boolean", multi='attendee'),
'attendee_status': fields.function(_compute, string='Attendee Status', type="selection", selection=calendar_attendee.STATE_SELECTION, multi='attendee'),
'display_time': fields.function(_compute, string='Event Time', type="char", multi='attendee'),
'date': fields.datetime('Date', states={'done': [('readonly', True)]}, required=True, track_visibility='onchange'),
'date_deadline': fields.datetime('End Date', states={'done': [('readonly', True)]}, required=True,),
'display_start': fields.function(_compute, string='Date', type="char", multi='display_start', store=True),
'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
'start': fields.function(_compute, string='Calculated start', type="datetime", multi='start', store=True, required=True),
'stop': fields.function(_compute, string='Calculated stop', type="datetime", multi='stop', store=True, required=True),
'start_date': fields.date('Start Date', states={'done': [('readonly', True)]}, track_visibility='onchange'),
'start_datetime': fields.datetime('Start DateTime', states={'done': [('readonly', True)]}, track_visibility='onchange'),
'stop_date': fields.date('End Date', states={'done': [('readonly', True)]}, track_visibility='onchange'),
'stop_datetime': fields.datetime('End Datetime', states={'done': [('readonly', True)]}, track_visibility='onchange'), # old date_deadline
'duration': fields.float('Duration', states={'done': [('readonly', True)]}),
'description': fields.text('Description', states={'done': [('readonly', True)]}),
'class': fields.selection([('public', 'Public'), ('private', 'Private'), ('confidential', 'Public for Employees')], 'Privacy', states={'done': [('readonly', True)]}),
'location': fields.char('Location', help="Location of Event", track_visibility='onchange', states={'done': [('readonly', True)]}),
'show_as': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Show Time as', states={'done': [('readonly', True)]}),
# RECURRENCE FIELD
'rrule': fields.function(_get_rulestring, type='char', fnct_inv=_set_rulestring, store=True, string='Recurrent Rule'),
'rrule_type': fields.selection([('daily', 'Day(s)'), ('weekly', 'Week(s)'), ('monthly', 'Month(s)'), ('yearly', 'Year(s)')], 'Recurrency', states={'done': [('readonly', True)]}, help="Let the event automatically repeat at that interval"),
'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"),
'recurrent_id': fields.integer('Recurrent ID'),
'recurrent_id_date': fields.datetime('Recurrent ID date'),
'vtimezone': fields.selection(_tz_get, string='Timezone'),
'end_type': fields.selection([('count', 'Number of repetitions'), ('end_date', 'End date')], 'Recurrence Termination'),
'interval': fields.integer('Repeat Every', help="Repeat every (Days/Week/Month/Year)"),
'count': fields.integer('Repeat', help="Repeat x times"),
@ -844,21 +910,21 @@ class calendar_event(osv.Model):
'day': fields.integer('Date of month'),
'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), ('WE', 'Wednesday'), ('TH', 'Thursday'), ('FR', 'Friday'), ('SA', 'Saturday'), ('SU', 'Sunday')], 'Weekday'),
'byday': fields.selection([('1', 'First'), ('2', 'Second'), ('3', 'Third'), ('4', 'Fourth'), ('5', 'Fifth'), ('-1', 'Last')], 'By day'),
'end_date': fields.date('Repeat Until'),
'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
'final_date': fields.date('Repeat Until'), # The last event of a recurrence
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
'color_partner_id': fields.related('user_id', 'partner_id', 'id', type="integer", string="colorize", store=False), # Color of creator
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the event alarm information without removing it."),
'categ_ids': fields.many2many('calendar.event.type', 'meeting_category_rel', 'event_id', 'type_id', 'Tags'),
'attendee_ids': fields.one2many('calendar.attendee', 'event_id', 'Attendees', ondelete='cascade'),
'partner_ids': fields.many2many('res.partner', string='Attendees', states={'done': [('readonly', True)]}),
'alarm_ids': fields.many2many('calendar.alarm', string='Reminders', ondelete="restrict"),
'partner_ids': fields.many2many('res.partner', 'calendar_event_res_partner_rel', string='Attendees', states={'done': [('readonly', True)]}),
'alarm_ids': fields.many2many('calendar.alarm', 'calendar_alarm_calendar_event_rel', string='Reminders', ondelete="restrict"),
}
_defaults = {
'end_type': 'count',
'count': 1,
'rrule_type': False,
'allday': False,
'state': 'draft',
'class': 'public',
'show_as': 'busy',
@ -871,58 +937,86 @@ class calendar_event(osv.Model):
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:
if event.stop < event.start:
return False
return True
_constraints = [
(_check_closing_date, 'Error ! End date cannot be set before start date.', ['date_deadline']),
(_check_closing_date, 'Error ! End date cannot be set before start date.', ['start', 'stop'])
]
def onchange_dates(self, cr, uid, ids, start_date, duration=False, end_date=False, allday=False, context=None):
"""Returns duration and/or end date based on values passed
@param ids: List of calendar event's IDs.
"""
if context is None:
context = {}
def onchange_allday(self, cr, uid, ids, start=False, end=False, starttime=False, endtime=False, startdatetime=False, enddatetime=False, checkallday=False, context=None):
value = {}
if not start_date:
if not ((starttime and endtime) or (start and end)): # At first intialize, we have not datetime
return value
if not end_date and not duration:
duration = 1.00
value['duration'] = duration
if checkallday: # from datetime to date
startdatetime = startdatetime or start
if startdatetime:
start = datetime.strptime(startdatetime, DEFAULT_SERVER_DATETIME_FORMAT)
value['start_date'] = datetime.strftime(start, DEFAULT_SERVER_DATE_FORMAT)
if allday: # For all day event
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
user = self.pool['res.users'].browse(cr, uid, uid)
enddatetime = enddatetime or end
if enddatetime:
end = datetime.strptime(enddatetime, DEFAULT_SERVER_DATETIME_FORMAT)
value['stop_date'] = datetime.strftime(end, DEFAULT_SERVER_DATE_FORMAT)
else: # from date to datetime
user = self.pool['res.users'].browse(cr, uid, uid, context)
tz = pytz.timezone(user.tz) if user.tz else pytz.utc
start = pytz.utc.localize(start).astimezone(tz) # convert start in user's timezone
start = start.astimezone(pytz.utc) # convert start back to utc
value['duration'] = 24.0
value['date'] = datetime.strftime(start, "%Y-%m-%d %H:%M:%S")
if starttime:
start = datetime.strptime(starttime.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT)
startdate = tz.localize(start) # Add "+hh:mm" timezone
startdate = startdate.replace(hour=8) # Set 8 AM in localtime
startdate = startdate.astimezone(pytz.utc) # Convert to UTC
value['start_datetime'] = datetime.strftime(startdate, DEFAULT_SERVER_DATETIME_FORMAT)
elif start:
value['start_datetime'] = start
if endtime:
end = datetime.strptime(endtime.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT)
enddate = tz.localize(end).replace(hour=18).astimezone(pytz.utc)
value['stop_datetime'] = datetime.strftime(enddate, DEFAULT_SERVER_DATETIME_FORMAT)
elif end:
value['stop_datetime'] = end
return {'value': value}
def onchange_dates(self, cr, uid, ids, fromtype, start=False, end=False, checkallday=False, allday=False, context=None):
"""Returns duration and end date based on values passed
@param ids: List of calendar event's IDs.
"""
value = {}
if checkallday != allday:
return value
value['allday'] = checkallday # Force to be rewrited
if allday:
if fromtype == 'start':
start = datetime.strptime(start, DEFAULT_SERVER_DATE_FORMAT)
value['start_datetime'] = datetime.strftime(start, DEFAULT_SERVER_DATETIME_FORMAT)
value['start'] = datetime.strftime(start, DEFAULT_SERVER_DATETIME_FORMAT)
if fromtype == 'stop':
end = datetime.strptime(end, DEFAULT_SERVER_DATE_FORMAT)
value['stop_datetime'] = datetime.strftime(end, DEFAULT_SERVER_DATETIME_FORMAT)
value['stop'] = datetime.strftime(end, DEFAULT_SERVER_DATETIME_FORMAT)
else:
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
if end_date and not duration:
end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
diff = end - start
duration = float(diff.days) * 24 + (float(diff.seconds) / 3600)
value['duration'] = round(duration, 2)
elif not end_date:
end = start + timedelta(hours=duration)
value['date_deadline'] = end.strftime("%Y-%m-%d %H:%M:%S")
elif end_date and duration and not allday:
# we have both, keep them synchronized:
# set duration based on end_date (arbitrary decision: this avoid
# getting dates like 06:31:48 instead of 06:32:00)
end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
diff = end - start
duration = float(diff.days) * 24 + (float(diff.seconds) / 3600)
value['duration'] = round(duration, 2)
if fromtype == 'start':
start = datetime.strptime(start, DEFAULT_SERVER_DATETIME_FORMAT)
value['start_date'] = datetime.strftime(start, DEFAULT_SERVER_DATE_FORMAT)
value['start'] = datetime.strftime(start, DEFAULT_SERVER_DATETIME_FORMAT)
if fromtype == 'stop':
end = datetime.strptime(end, DEFAULT_SERVER_DATETIME_FORMAT)
value['stop_date'] = datetime.strftime(end, DEFAULT_SERVER_DATE_FORMAT)
value['stop'] = datetime.strftime(end, DEFAULT_SERVER_DATETIME_FORMAT)
return {'value': value}
@ -1008,7 +1102,6 @@ class calendar_event(osv.Model):
@param order: The fields (comma separated, format "FIELD {DESC|ASC}") on which the events should be sorted
"""
if not context:
context = {}
@ -1033,7 +1126,6 @@ class calendar_event(osv.Model):
result.append(ev.id)
result_data.append(self.get_search_fields(ev, order_fields))
continue
rdates = self.get_recurrent_date_by_event(cr, uid, ev, context=context)
for r_date in rdates:
@ -1044,7 +1136,7 @@ class calendar_event(osv.Model):
pile = []
ok = True
for arg in domain:
if str(arg[0]) in (str('date'), str('date_deadline'), str('end_date')):
if str(arg[0]) in ('start', 'stop', 'final_date'):
if (arg[1] == '='):
ok = r_date.strftime('%Y-%m-%d') == arg[2]
if (arg[1] == '>'):
@ -1107,8 +1199,6 @@ class calendar_event(osv.Model):
weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
if freq == 'weekly':
byday = map(lambda x: x.upper(), filter(lambda x: data.get(x) and x in weekdays, data))
# byday = map(lambda x: x.upper(),[data[day] for day in weekdays if data[day]])
if byday:
return ';BYDAY=' + ','.join(byday)
return ''
@ -1125,8 +1215,8 @@ class calendar_event(osv.Model):
return ''
def get_end_date(data):
if data.get('end_date'):
data['end_date_new'] = ''.join((re.compile('\d')).findall(data.get('end_date'))) + 'T235959Z'
if data.get('final_date'):
data['end_date_new'] = ''.join((re.compile('\d')).findall(data.get('final_date'))) + 'T235959Z'
return (data.get('end_type') == 'count' and (';COUNT=' + str(data.get('count'))) or '') +\
((data.get('end_date_new') and data.get('end_type') == 'end_date' and (';UNTIL=' + data.get('end_date_new'))) or '')
@ -1143,7 +1233,7 @@ class calendar_event(osv.Model):
return {
'byday': False,
'recurrency': False,
'end_date': False,
'final_date': False,
'rrule_type': False,
'month_by': False,
'interval': 0,
@ -1163,14 +1253,13 @@ class calendar_event(osv.Model):
def _parse_rrule(self, rule, data, date_start):
day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
rrule_type = ['yearly', 'monthly', 'weekly', 'daily']
r = rrule.rrulestr(rule, dtstart=datetime.strptime(date_start, "%Y-%m-%d %H:%M:%S"))
r = rrule.rrulestr(rule, dtstart=datetime.strptime(date_start, DEFAULT_SERVER_DATETIME_FORMAT))
if r._freq > 0 and r._freq < 4:
data['rrule_type'] = rrule_type[r._freq]
data['count'] = r._count
data['interval'] = r._interval
data['end_date'] = r._until and r._until.strftime("%Y-%m-%d %H:%M:%S")
data['final_date'] = r._until and r._until.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
#repeat weekly
if r._byweekday:
for i in xrange(0, 7):
@ -1196,12 +1285,12 @@ class calendar_event(osv.Model):
#FIXEME handle forever case
#end of recurrence
#in case of repeat for ever that we do not support right now
if not (data.get('count') or data.get('end_date')):
if not (data.get('count') or data.get('final_date')):
data['count'] = 100
if data.get('count'):
data['end_type'] = 'count'
else:
data['end_type'] = 'end_date'
data['end_type'] = 'final_date'
return data
def message_get_subscription_data(self, cr, uid, ids, user_pid=None, context=None):
@ -1225,25 +1314,6 @@ class calendar_event(osv.Model):
res.update(self.check_partners_email(cr, uid, value[0][2], context=context))
return res
def onchange_rec_day(self, cr, uid, id, date, mo, tu, we, th, fr, sa, su):
""" set the start date according to the first occurence of rrule"""
rrule_obj = self._get_empty_rrule_data()
rrule_obj.update({
'byday': True,
'rrule_type': 'weekly',
'mo': mo,
'tu': tu,
'we': we,
'th': th,
'fr': fr,
'sa': sa,
'su': su,
'interval': 1
})
str_rrule = self.compute_rule_string(rrule_obj)
first_occurence = list(rrule.rrulestr(str_rrule + ";COUNT=1", dtstart=datetime.strptime(date, "%Y-%m-%d %H:%M:%S"), forceset=True))[0]
return {'value': {'date': first_occurence.strftime("%Y-%m-%d") + ' 00:00:00'}}
def check_partners_email(self, cr, uid, partner_ids, context=None):
""" Verify that selected partner_ids have an email_address defined.
Otherwise throw a warning. """
@ -1261,14 +1331,13 @@ class calendar_event(osv.Model):
'message': warning_msg,
}}
# ----------------------------------------
# OpenChatter
# ----------------------------------------
# shows events of the day for this user
def _needaction_domain_get(self, cr, uid, context=None):
return [('end_date', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')), ('date', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')), ('user_id', '=', uid)]
return [
('stop', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')),
('start', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 00:00:00')),
('user_id', '=', uid),
]
def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
if isinstance(thread_id, str):
@ -1290,7 +1359,7 @@ class calendar_event(osv.Model):
# Used for view in controller
invitation = {'meeting': {}, 'attendee': []}
meeting = self.browse(cr, uid, int(meeting_id), context)
meeting = self.browse(cr, uid, int(meeting_id), context=context)
invitation['meeting'] = {
'event': meeting.name,
'where': meeting.location,
@ -1316,7 +1385,8 @@ class calendar_event(osv.Model):
elif interval == 'dayname':
res = date.strftime('%A')
elif interval == 'time':
res = date.strftime('%I:%M %p')
dummy, format_time = self.get_date_formats(cr, uid, context=context)
res = date.strftime(format_time + " %Z")
return res
def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False):
@ -1325,15 +1395,15 @@ class calendar_event(osv.Model):
if context.get('mymeetings', False):
partner_id = self.pool['res.users'].browse(cr, uid, uid, context).partner_id.id
args += ['|', ('partner_ids', 'in', [partner_id]), ('user_id', '=', uid)]
args += [('partner_ids', 'in', [partner_id])]
new_args = []
for arg in args:
new_arg = arg
if arg[0] in ('date', unicode('date')) and arg[1] == ">=":
if arg[0] in ('start_date', 'start_datetime', 'start',) and arg[1] == ">=":
if context.get('virtual_id', True):
new_args += ['|', '&', ('recurrency', '=', 1), ('end_date', arg[1], arg[2])]
new_args += ['|', '&', ('recurrency', '=', 1), ('final_date', arg[1], arg[2])]
elif arg[0] == "id":
new_id = get_real_ids(arg[2])
new_arg = (arg[0], arg[1], new_id)
@ -1356,6 +1426,8 @@ class calendar_event(osv.Model):
context = {}
default = default or {}
self._set_date(cr, uid, default, id=default.get('id'), context=context)
default['attendee_ids'] = False
res = super(calendar_event, self).copy(cr, uid, calendar_id2real_id(id), default, context)
@ -1363,17 +1435,18 @@ class calendar_event(osv.Model):
def _detach_one_event(self, cr, uid, id, values=dict(), context=None):
real_event_id = calendar_id2real_id(id)
data = self.read(cr, uid, id, ['date', 'date_deadline', 'rrule', 'duration'])
data = self.read(cr, uid, id, ['allday', 'start', 'stop', 'rrule', 'duration'])
data['start_date' if data['allday'] else 'start_datetime'] = data['start']
data['stop_date' if data['allday'] else 'stop_datetime'] = data['stop']
if data.get('rrule'):
data.update(
values,
recurrent_id=real_event_id,
recurrent_id_date=data.get('date'),
recurrent_id_date=data.get('start'),
rrule_type=False,
rrule='',
recurrency=False,
end_date=datetime.strptime(values.get('date', False) or data.get('date'), "%Y-%m-%d %H:%M:%S") + timedelta(hours=values.get('duration', False) or data.get('duration'))
final_date=datetime.strptime(data.get('start'), DEFAULT_SERVER_DATETIME_FORMAT if data['allday'] else DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=values.get('duration', False) or data.get('duration'))
)
#do not copy the id
@ -1408,17 +1481,22 @@ class calendar_event(osv.Model):
def _only_changes_to_apply_on_real_ids(field_names):
''' return True if changes are only to be made on the real ids'''
for field in field_names:
if field in ['date', 'active']:
if field in ['start', 'start_date', 'start_datetime', 'stop', 'stop_date', 'stop_datetime', 'active']:
return True
return False
context = context or {}
if not isinstance(ids, (tuple, list)):
ids = [ids]
if isinstance(ids, (str, int, long)):
if len(str(ids).split('-')) == 1:
ids = [int(ids)]
else:
ids = [ids]
context = context or {}
self._set_date(cr, uid, values, id=ids[0], context=context)
for one_ids in ids:
if isinstance(one_ids, (str, int, long)):
if len(str(one_ids).split('-')) == 1:
ids = [int(one_ids)]
else:
ids = [one_ids]
res = False
new_id = False
@ -1438,7 +1516,7 @@ class calendar_event(osv.Model):
ids.append(real_event_id)
continue
else:
data = self.read(cr, uid, event_id, ['date', 'date_deadline', 'rrule', 'duration'])
data = self.read(cr, uid, event_id, ['start', 'stop', 'rrule', 'duration'])
if data.get('rrule'):
new_id = self._detach_one_event(cr, uid, event_id, values, context=None)
@ -1446,16 +1524,16 @@ class calendar_event(osv.Model):
# set end_date for calendar searching
if values.get('recurrency', True) and values.get('end_type', 'count') in ('count', unicode('count')) and \
(values.get('rrule_type') or values.get('count') or values.get('date') or values.get('date_deadline')):
for data in self.read(cr, uid, ids, ['end_date', 'date_deadline', 'recurrency', 'rrule_type', 'count', 'end_type'], context=context):
end_date = self._get_recurrency_end_date(data, context=context)
super(calendar_event, self).write(cr, uid, [data['id']], {'end_date': end_date}, context=context)
(values.get('rrule_type') or values.get('count') or values.get('start') or values.get('stop')):
for id in ids:
final_date = self._get_recurrency_end_date(cr, uid, id, context=context)
super(calendar_event, self).write(cr, uid, [id], {'final_date': final_date}, context=context)
attendees_create = False
if values.get('partner_ids', False):
attendees_create = self.create_attendees(cr, uid, ids, context)
if values.get('date', False) and values.get('active', True):
if (values.get('start_date') or values.get('start_datetime', False)) and values.get('active', True):
the_id = new_id or (ids and int(ids[0]))
if attendees_create:
@ -1468,21 +1546,20 @@ class calendar_event(osv.Model):
current_user = self.pool['res.users'].browse(cr, uid, uid, context=context)
if self.pool['calendar.attendee']._send_mail_to_attendees(cr, uid, mail_to_ids, template_xmlid='calendar_template_meeting_changedate', email_from=current_user.email, context=context):
self.message_post(cr, uid, the_id, body=_("A email has been send to specify that the date has been changed !"), subtype="calendar.subtype_invitation", context=context)
return res or True and False
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
self._set_date(cr, uid, vals, id=False, context=context)
if not 'user_id' in vals: # Else bug with quick_create when we are filter on an other user
vals['user_id'] = uid
res = super(calendar_event, self).create(cr, uid, vals, context=context)
data = self.read(cr, uid, [res], ['end_date', 'date_deadline', 'recurrency', 'rrule_type', 'count', 'end_type'], context=context)[0]
end_date = self._get_recurrency_end_date(data, context=context)
self.write(cr, uid, [res], {'end_date': end_date}, context=context)
final_date = self._get_recurrency_end_date(cr, uid, res, context=context)
self.write(cr, uid, [res], {'final_date': final_date}, context=context)
self.create_attendees(cr, uid, [res], context=context)
return res
@ -1508,28 +1585,35 @@ class calendar_event(osv.Model):
if context is None:
context = {}
fields2 = fields and fields[:] or None
EXTRAFIELDS = ('class', 'user_id', 'duration', 'date', 'rrule', 'vtimezone')
EXTRAFIELDS = ('class', 'user_id', 'duration', 'allday', 'start', 'start_date', 'start_datetime', 'rrule')
for f in EXTRAFIELDS:
if fields and (f not in fields):
fields2.append(f)
if isinstance(ids, (str, int, long)):
select = [ids]
else:
select = ids
select = map(lambda x: (x, calendar_id2real_id(x)), select)
result = []
real_data = super(calendar_event, self).read(cr, uid, [real_id for calendar_id, real_id in select], fields=fields2, context=context, load=load)
real_data = dict(zip([x['id'] for x in real_data], real_data))
for calendar_id, real_id in select:
res = real_data[real_id].copy()
ls = calendar_id2real_id(calendar_id, with_date=res and res.get('duration', 0) or 0)
ls = calendar_id2real_id(calendar_id, with_date=res and res.get('duration', 0) > 0 and res.get('duration') or 1)
if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
res['date'] = ls[1]
res['date_deadline'] = ls[2]
res['start'] = ls[1]
res['stop'] = ls[2]
if res['allday']:
res['start_date'] = ls[1]
res['stop_date'] = ls[2]
else:
res['start_datetime'] = ls[1]
res['stop_datetime'] = ls[2]
res['display_time'] = self._get_display_time(cr, uid, ls[1], ls[2], res['duration'], res['allday'], context=context)
res['id'] = calendar_id
result.append(res)
@ -1540,7 +1624,7 @@ class calendar_event(osv.Model):
continue
if r['class'] == 'private':
for f in r.keys():
if f not in ('id', 'date', 'date_deadline', 'duration', 'user_id', 'state', 'interval', 'count', 'recurrent_id_date'):
if f not in ('id', 'allday', 'start', 'stop', 'duration', 'user_id', 'state', 'interval', 'count', 'recurrent_id_date'):
if isinstance(r[f], list):
r[f] = []
else:
@ -1552,12 +1636,11 @@ class calendar_event(osv.Model):
for k in EXTRAFIELDS:
if (k in r) and (fields and (k not in fields)):
del r[k]
if isinstance(ids, (str, int, long)):
return result and result[0] or False
return result
def unlink(self, cr, uid, ids, unlink_level=0, context=None):
def unlink(self, cr, uid, ids, can_be_deleted=True, context=None):
if not isinstance(ids, list):
ids = [ids]
res = False
@ -1565,13 +1648,12 @@ class calendar_event(osv.Model):
ids_to_exclure = []
ids_to_unlink = []
# One time moved to google_Calendar, we can specify, if not in google, and not rec or get_inst = 0, we delete it
for event_id in ids:
if unlink_level == 1 and len(str(event_id).split('-')) == 1: # if ID REAL
if self.browse(cr, uid, event_id).recurrent_id:
if can_be_deleted and len(str(event_id).split('-')) == 1: # if ID REAL
if self.browse(cr, uid, int(event_id), context).recurrent_id:
ids_to_exclure.append(event_id)
else:
ids_to_unlink.append(event_id)
ids_to_unlink.append(int(event_id))
else:
ids_to_exclure.append(event_id)
@ -1651,6 +1733,7 @@ class ir_http(osv.AbstractModel):
if error_message:
raise BadRequest(error_message)
return True

View File

@ -142,12 +142,12 @@
<table>
<tr>
<td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.date, 'dayname')}</div>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname')}</div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
${object.event_id.get_interval(object.event_id.date,'day')}
${object.event_id.get_interval(object.event_id.start,'day')}
</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.date, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.date, 'time', tz=object.partner_id.tz) or ''}</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
</td>
<td>
<table cellspacing="0" cellpadding="0" border="0" style="margin-top: 15px; margin-left: 10px;font-size: 16px;">
@ -269,18 +269,18 @@
<div style="height: 50px;text-align: left;font-size : 14px;border-collapse: separate;margin-top:10px">
<strong style="margin-left:12px">Dear ${object.cn}</strong> ,<br/>
<p style="margin-left:12px">The date of the meeting has been changed...<br/>
The meeting created by ${object.event_id.user_id.partner_id.name} is now scheduled for : ${object.event_id.display_time}.</p>
The meeting created by ${object.event_id.user_id.partner_id.name} is now scheduled for : ${object.event_id.get_display_time_tz(tz=object.partner_id.tz)}.</p>
</div>
<div style="height: auto;margin-left:12px;margin-top:30px;">
<table>
<tr>
<td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.date, 'dayname')}</div>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname')}</div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
${object.event_id.get_interval(object.event_id.date,'day')}
${object.event_id.get_interval(object.event_id.start,'day')}
</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.date, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.date, 'time', tz=object.partner_id.tz) or ''}</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
</td>
<td>
<table cellspacing="0" cellpadding="0" border="0" style="margin-top: 15px; margin-left: 10px;font-size: 16px;">
@ -407,12 +407,12 @@
<table>
<tr>
<td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.date, 'dayname')}</div>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname')}</div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
${object.event_id.get_interval(object.event_id.date,'day')}
${object.event_id.get_interval(object.event_id.start,'day')}
</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.date, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.date, 'time', tz=object.partner_id.tz) or ''}</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
</td>
<td>
<table cellspacing="0" cellpadding="0" border="0" style="margin-top: 15px; margin-left: 10px;font-size: 16px;">

View File

@ -23,22 +23,24 @@
<field name="partner_ids" eval="[(6,0,[ref('base.res_partner_6')])]"/>
<field name="name">Follow-up for Project proposal</field>
<field name="description">Meeting to discuss project plan and hash out the details of implementation.</field>
<field eval="time.strftime('%Y-%m-03 10:20:00')" name="date"/>
<field eval="time.strftime('%Y-%m-03 10:20:00')" name="start"/>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1')])]"/>
<field eval="time.strftime('%Y-%m-03 16:30:00')" name="date_deadline"/>
<field eval="time.strftime('%Y-%m-03 16:30:00')" name="stop"/>
<field eval="6.3" name="duration"/>
<field eval="0" name="allday"/>
<field name="state">open</field>
</record>
<record id="calendar_event_2" model="calendar.event">
<field eval="1" name="active"/>
<field eval="1" name="active"/>
<field name="user_id" ref="base.user_demo"/>
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root'),ref('base.res_partner_4'),ref('base.res_partner_5')])]"/>
<field name="name">Initial discussion</field>
<field name="description">Discussion with partner for product.</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet3')])]"/>
<field eval="time.strftime('%Y-%m-05 12:00:00')" name="date"/>
<field eval="time.strftime('%Y-%m-05 19:00:00')" name="date_deadline"/>
<field eval="time.strftime('%Y-%m-05 12:00:00')" name="start"/>
<field eval="time.strftime('%Y-%m-05 19:00:00')" name="stop"/>
<field eval="0" name="allday"/>
<field eval="7.0" name="duration"/>
<field name="state">draft</field>
</record>
@ -49,9 +51,10 @@
<field name="name">Pricing Discussion</field>
<field name="description">Internal meeting for discussion for new pricing for product and services.</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1'), ref('categ_meet2')])]"/>
<field eval="time.strftime('%Y-%m-12 15:55:05')" name="date"/>
<field eval="time.strftime('%Y-%m-12 18:55:05')" name="date_deadline"/>
<field eval="time.strftime('%Y-%m-12 15:55:05')" name="start"/>
<field eval="time.strftime('%Y-%m-12 18:55:05')" name="stop"/>
<field eval="3.0" name="duration"/>
<field eval="0" name="allday"/>
<field name="state">open</field>
</record>
@ -61,9 +64,10 @@
<field name="partner_ids" eval="[(6,0,[ref('base.partner_demo'),ref('base.res_partner_6')])]"/>
<field name="name">Requirements review</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet3')])]"/>
<field eval="time.strftime('%Y-%m-20 8:00:00')" name="date"/>
<field eval="time.strftime('%Y-%m-20 10:30:00')" name="date_deadline"/>
<field eval="time.strftime('%Y-%m-20 8:00:00')" name="start"/>
<field eval="time.strftime('%Y-%m-20 10:30:00')" name="stop"/>
<field eval="2.5" name="duration"/>
<field eval="0" name="allday"/>
<field name="state">open</field>
</record>
@ -72,9 +76,9 @@
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root'),ref('base.res_partner_8')])]"/>
<field name="name">Changes in Designing</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1')])]"/>
<field eval="time.strftime('%Y-%m-22 11:05:00')" name="date"/>
<field eval="time.strftime('%Y-%m-22 16:05:00')" name="date_deadline"/>
<field eval="5" name="duration"/>
<field eval="time.strftime('%Y-%m-22')" name="start"/>
<field eval="time.strftime('%Y-%m-22')" name="stop"/>
<field eval="1" name="allday"/>
<field name="state">open</field>
</record>
@ -84,9 +88,10 @@
<field name="partner_ids" eval="[(6,0,[ref('base.partner_root'),ref('base.res_partner_1'),ref('base.res_partner_4'),ref('base.res_partner_6'),ref('base.res_partner_8')])]"/>
<field name="name">Presentation for new Services</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1'), ref('categ_meet2')])]"/>
<field eval="time.strftime('%Y-%m-18 2:00:00')" name="date"/>
<field eval="time.strftime('%Y-%m-18 10:30:00')" name="date_deadline"/>
<field eval="time.strftime('%Y-%m-18 2:00:00')" name="start"/>
<field eval="time.strftime('%Y-%m-18 10:30:00')" name="stop"/>
<field eval="8.5" name="duration"/>
<field eval="0" name="allday"/>
<field name="state">draft</field>
</record>
@ -96,9 +101,10 @@
<field name="partner_ids" eval="[(6,0,[ref('base.res_partner_7')])]"/>
<field name="name">Presentation of the new Calendar</field>
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1'), ref('categ_meet2')])]"/>
<field eval="time.strftime('%Y-%m-16 6:00:00')" name="date"/>
<field eval="time.strftime('%Y-%m-16 18:30:00')" name="date_deadline"/>
<field eval="time.strftime('%Y-%m-16')" name="start"/>
<field eval="time.strftime('%Y-%m-16')" name="stop"/>
<field eval="8.5" name="duration"/>
<field eval="1" name="allday"/>
<field name="state">draft</field>
</record>

View File

@ -9,7 +9,7 @@
<field name="name">Meeting Types Tree</field>
<field name="model">calendar.event.type</field>
<field name="arch" type="xml">
<tree string="Meeting Types">
<tree string="Meeting Types" editable="bottom">
<field name="name"/>
</tree>
</field>
@ -58,24 +58,24 @@
</group>
<group>
<group>
<field name="date" string="Starting at" on_change="onchange_dates(date, duration, False, allday)"/>
<label for="duration"/>
<field name="start" attrs="{'invisible': True}"/>
<field name="stop" attrs="{'invisible': True}"/>
<field name="start_date" string="Starting at" on_change="onchange_dates('start', start_date, stop_date, allday, True)" attrs="{'invisible': [('allday','=',False)]}"/>
<field name="stop_date" string="Ending at" on_change="onchange_dates('stop', start_date, stop_date, allday, True)" attrs="{'invisible': [('allday','=',False)]}"/>
<field name="start_datetime" string="Starting at" on_change="onchange_dates('start', start_datetime, stop_datetime, allday, False)" attrs="{'invisible': [('allday','=',True)]}"/>
<field name="stop_datetime" string="Ending at" on_change="onchange_dates('stop', start_datetime, stop_datetime, allday, False)" attrs="{'invisible': [('allday','=',True)]}"/>
<label for="allday"/>
<div>
<field name="duration" widget="float_time"
on_change="onchange_dates(date, duration, False, allday)"
class="oe_inline" attrs="{'invisible': [('allday','=',True)]}"/>
<label string="hours" attrs="{'invisible': [('allday','=',True)]}"/>
(<field name="allday" class="oe_inline"/>
<label for="allday" string="All Day?"/>)
<field name="allday" class="oe_inline" on_change="onchange_allday(start, stop, start_date, stop_date, start_datetime, stop_datetime, allday)"/>
</div>
<field name="date_deadline" groups="base.group_no_one"
attrs="{'invisible': ['|', ('allday','=',True), ('duration','&lt;', 24)]}"
/>
<field name="duration" widget="float_time" class="oe_inline" attrs="{ 'invisible': True }"/>
</group>
<group>
<field name="categ_ids" widget="many2many_tags"/>
<field name="categ_ids" widget="many2many_tags" />
<field name="alarm_ids" widget="many2many_tags" />
<field name="location"/>
<field name="location" />
</group>
</group>
@ -98,17 +98,17 @@
<div>
<field name="end_type" attrs="{'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="count" attrs="{'invisible': [('end_type', '!=', 'count')], 'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="end_date" attrs="{'invisible': [('end_type', '!=', 'end_date')], 'required': [('end_type', '=', 'end_date')]}" class="oe_inline"/>
<field name="final_date" attrs="{'invisible': [('end_type', '!=', 'end_date')], 'required': [('end_type', '=', 'end_date')]}" class="oe_inline"/>
</div>
<label string="Select Weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}"/>
<group col="2" colspan="1" name="weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}" >
<field name="mo" on_change="onchange_rec_day(date,mo,tu,we,th,fr,sa,su)"/>
<field name="tu" on_change="onchange_rec_day(date,mo,tu,we,th,fr,sa,su)"/>
<field name="we" on_change="onchange_rec_day(date,mo,tu,we,th,fr,sa,su)"/>
<field name="th" on_change="onchange_rec_day(date,mo,tu,we,th,fr,sa,su)"/>
<field name="fr" on_change="onchange_rec_day(date,mo,tu,we,th,fr,sa,su)"/>
<field name="sa" on_change="onchange_rec_day(date,mo,tu,we,th,fr,sa,su)"/>
<field name="su" on_change="onchange_rec_day(date,mo,tu,we,th,fr,sa,su)"/>
<field name="mo" />
<field name="tu" />
<field name="we" />
<field name="th" />
<field name="fr" />
<field name="sa" />
<field name="su" />
</group>
<label string="Day of Month"
@ -178,7 +178,9 @@
<field name="name"/>
<group>
<group>
<field name="date" string="Start" />
<field name="start_date" string="Starting at" attrs="{'invisible': [('allday','=',False)]}"/>
<field name="start_datetime" string="Starting at" attrs="{'invisible': [('allday','=',True)]}"/>
<field name="duration" string="Duration" widget="float_time" attrs="{'invisible': [('allday','=',True)]}"/>
<field name="allday" class="oe_inline" attrs="{'invisible': [('allday','=',False)]}"/>
<field name="partner_ids" widget="many2manyattendee" string="Attendees"/>
@ -201,7 +203,8 @@
<field name="arch" type="xml">
<tree string="Meetings" fonts="bold:message_unread==True">
<field name="name" string="Subject"/>
<field name="date" string="Event Date"/>
<field name="allday" invisible="True"/>
<field name="display_start" string="Date" />
<field name="user_id"/>
<field name="location"/>
<field name="show_as"/>
@ -219,7 +222,7 @@
<field name="model">calendar.event</field>
<field name="priority" eval="2"/>
<field name="arch" type="xml">
<calendar string="Meetings" date_start="date" date_stop="date_deadline" date_delay="duration" all_day="allday"
<calendar string="Meetings" date_start="start" date_stop="stop" date_delay="duration" all_day="allday"
display="[name]" color="color_partner_id" attendee="partner_ids" avatar_model="res.partner"
use_contacts="True" event_open_popup="%(calendar.view_calendar_event_form_popup)s">
@ -260,8 +263,7 @@
<group expand="0" string="Group By...">
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Availability" icon="terp-camera_test" domain="[]" context="{'group_by':'show_as'}"/>
<filter string="Privacy" icon="terp-locked" domain="[]" context="{'group_by':'class'}"/>
<filter string="Event Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" help="Start Date of Event by Month"/>
<filter string="Privacy" icon="terp-locked" domain="[]" context="{'group_by':'class'}"/>
</group>
</search>
</field>

View File

@ -33,22 +33,23 @@ class meeting_invitation(http.Controller):
registry = openerp.modules.registry.RegistryManager.get(db)
meeting_pool = registry.get('calendar.event')
attendee_pool = registry.get('calendar.attendee')
partner_pool = registry.get('res.partner')
with registry.cursor() as cr:
attendee_data = meeting_pool.get_attendee(cr, openerp.SUPERUSER_ID, id)
attendee = attendee_pool.search_read(cr, openerp.SUPERUSER_ID, [('access_token', '=', token)], [])
if attendee and attendee[0] and attendee[0].get('partner_id'):
partner_id = int(attendee[0].get('partner_id')[0])
tz = partner_pool.read(cr, openerp.SUPERUSER_ID, partner_id, ['tz'])['tz']
else:
tz = False
attendee_data = meeting_pool.get_attendee(cr, openerp.SUPERUSER_ID, id, dict(tz=tz))
if attendee:
attendee_data['current_attendee'] = attendee[0]
css = '<link rel="stylesheet" href="/web/css/web.assets_backend"/>'
js = '<script type="text/javascript" src="/web/js/web.assets_backend"></script>'
return webmain.html_template % {
'js': js,
'css': css,
'modules': simplejson.dumps(webmain.module_boot(db)),
'init': "s.calendar.event('%s', '%s', '%s', '%s' , '%s');" % (db, action, id, 'form', json.dumps(attendee_data)),
}
values = dict(init="s.calendar.event('%s', '%s', '%s', '%s' , '%s');" % (db, action, id, 'form', json.dumps(attendee_data)))
return request.render('web.webclient_bootstrap', values)
# Function used, in RPC to check every 5 minutes, if notification to do for an event or not
@http.route('/calendar/notify', type='json', auth="none")

View File

@ -8,14 +8,15 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2013-06-29 05:34+0000\n"
"Last-Translator: DWXXX <Unknown>\n"
"PO-Revision-Date: 2014-05-07 01:56+0000\n"
"Last-Translator: Victor Yu <vivianyw@163.com>\n"
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:38+0000\n"
"X-Generator: Launchpad (build 16985)\n"
"X-Launchpad-Export-Date: 2014-05-07 06:27+0000\n"
"X-Generator: Launchpad (build 16996)\n"
"Language: zh_CN\n"
#. module: base_calendar
#: selection:calendar.alarm,trigger_related:0
@ -35,7 +36,7 @@ msgstr "我的事件"
msgid ""
"This property defines the list of date/time exceptions for a recurring "
"calendar component."
msgstr "属性定义循环日程的日期/时间异常列表。"
msgstr "属性定义循环日程的日期/时间异常列表。"
#. module: base_calendar
#: selection:calendar.event,rrule_type:0
@ -49,7 +50,7 @@ msgstr "周"
#: field:calendar.todo,we:0
#: field:crm.meeting,we:0
msgid "Wed"
msgstr "星期三"
msgstr "三"
#. module: base_calendar
#: selection:calendar.attendee,cutype:0
@ -79,7 +80,7 @@ msgstr "提醒"
#: selection:calendar.todo,week_list:0
#: selection:crm.meeting,week_list:0
msgid "Sunday"
msgstr "星期"
msgstr "星期"
#. module: base_calendar
#: field:calendar.attendee,role:0
@ -137,7 +138,7 @@ msgstr "3月"
#. module: base_calendar
#: help:calendar.attendee,cutype:0
msgid "Specify the type of Invitation"
msgstr "指定类型的邀请"
msgstr "指定邀请类型"
#. module: base_calendar
#: view:crm.meeting:0
@ -177,7 +178,7 @@ msgstr "空闲"
#. module: base_calendar
#: help:crm.meeting,message_unread:0
msgid "If checked new messages require your attention."
msgstr "如果要求你关注新消息,勾选此项"
msgstr "如果勾选此项,需要你关注新消息。"
#. module: base_calendar
#: help:calendar.attendee,rsvp:0
@ -197,34 +198,34 @@ msgstr "原来请求委派给的用户"
#. module: base_calendar
#: field:calendar.attendee,ref:0
msgid "Event Ref"
msgstr "事件"
msgstr "参考事件"
#. module: base_calendar
#: field:calendar.event,tu:0
#: field:calendar.todo,tu:0
#: field:crm.meeting,tu:0
msgid "Tue"
msgstr "星期二"
msgstr "二"
#. module: base_calendar
#: selection:calendar.event,byday:0
#: selection:calendar.todo,byday:0
#: selection:crm.meeting,byday:0
msgid "Third"
msgstr "第三"
msgstr "第三"
#. module: base_calendar
#: selection:calendar.alarm,trigger_related:0
#: selection:res.alarm,trigger_related:0
msgid "The event ends"
msgstr "这活动结束时"
msgstr "事件结束"
#. module: base_calendar
#: selection:calendar.event,byday:0
#: selection:calendar.todo,byday:0
#: selection:crm.meeting,byday:0
msgid "Last"
msgstr "最近"
msgstr "持续"
#. module: base_calendar
#: help:crm.meeting,message_ids:0
@ -295,7 +296,7 @@ msgstr "显示"
#. module: base_calendar
#: help:calendar.attendee,state:0
msgid "Status of the attendee's participation"
msgstr "与会者的参与状况"
msgstr "与会者的出席状况"
#. module: base_calendar
#: view:crm.meeting:0
@ -344,19 +345,19 @@ msgstr "循环日期"
#: field:calendar.alarm,event_end_date:0
#: field:calendar.attendee,event_end_date:0
msgid "Event End Date"
msgstr "活动结束日期"
msgstr "事件结束日期"
#. module: base_calendar
#: selection:calendar.attendee,role:0
msgid "Optional Participation"
msgstr "可选择参与"
msgstr "可选择参与"
#. module: base_calendar
#: help:crm.meeting,message_summary:0
msgid ""
"Holds the Chatter summary (number of messages, ...). This summary is "
"directly in html format in order to be inserted in kanban views."
msgstr "保存复杂的摘要(消息数量,……等)。为了插入到看板视图,这一摘要直接是HTML格式。"
msgstr "保存聊天摘要(消息数量,……等)。为了插入到看板视图这一摘要直接是HTML格式。"
#. module: base_calendar
#: code:addons/base_calendar/base_calendar.py:399
@ -375,7 +376,7 @@ msgstr "警告!"
msgid ""
"If the active field is set to true, it will allow you to hide the "
"event alarm information without removing it."
msgstr "如果去掉这个勾,可以不需要删除就隐藏事件提醒信息"
msgstr "如果去掉这个勾,可以隐藏事件提醒信息而不必删除事件。"
#. module: base_calendar
#: field:calendar.alarm,repeat:0
@ -461,12 +462,12 @@ msgstr "邀请类型"
#. module: base_calendar
#: view:res.alarm:0
msgid "Reminder details"
msgstr "细节提醒"
msgstr "提醒细节"
#. module: base_calendar
#: field:calendar.attendee,parent_ids:0
msgid "Delegrated From"
msgstr "参加者"
msgstr "委派者"
#. module: base_calendar
#: selection:calendar.event,select1:0
@ -490,7 +491,7 @@ msgstr "地点"
#. module: base_calendar
#: selection:calendar.attendee,role:0
msgid "Participation required"
msgstr "参加要求"
msgstr "要求参加"
#. module: base_calendar
#: view:calendar.event:0
@ -554,7 +555,7 @@ msgstr "可见"
#. module: base_calendar
#: field:calendar.attendee,rsvp:0
msgid "Required Reply?"
msgstr "要求复吗?"
msgstr "要求复吗?"
#. module: base_calendar
#: field:calendar.event,base_calendar_url:0
@ -595,7 +596,7 @@ msgstr "会议详情"
#. module: base_calendar
#: field:calendar.attendee,child_ids:0
msgid "Delegrated To"
msgstr "代表人"
msgstr "委派给"
#. module: base_calendar
#: code:addons/base_calendar/crm_meeting.py:102
@ -650,7 +651,7 @@ msgstr "语言"
#: field:calendar.todo,end_date:0
#: field:crm.meeting,end_date:0
msgid "Repeat Until"
msgstr "重复到"
msgstr "重复到"
#. module: base_calendar
#: view:crm.meeting:0
@ -662,7 +663,7 @@ msgstr "选项"
#: selection:calendar.todo,byday:0
#: selection:crm.meeting,byday:0
msgid "First"
msgstr "第一"
msgstr "第一"
#. module: base_calendar
#: view:calendar.event:0
@ -689,7 +690,7 @@ msgstr "12月"
#: selection:calendar.todo,week_list:0
#: selection:crm.meeting,week_list:0
msgid "Tuesday"
msgstr "二"
msgstr "星期二"
#. module: base_calendar
#: field:crm.meeting,categ_ids:0
@ -704,7 +705,7 @@ msgstr "可用"
#. module: base_calendar
#: selection:calendar.attendee,cutype:0
msgid "Individual"
msgstr "个人"
msgstr "单独"
#. module: base_calendar
#: help:calendar.event,count:0
@ -770,7 +771,7 @@ msgstr "私有"
#: field:calendar.todo,class:0
#: field:crm.meeting,class:0
msgid "Privacy"
msgstr "隐私"
msgstr "隐私"
#. module: base_calendar
#: model:ir.model,name:base_calendar.model_res_alarm
@ -821,7 +822,7 @@ msgstr "附件"
#. module: base_calendar
#: field:crm.meeting,date_closed:0
msgid "Closed"
msgstr "关闭"
msgstr "关闭"
#. module: base_calendar
#: view:calendar.event:0
@ -868,7 +869,7 @@ msgstr "状态"
#. module: base_calendar
#: help:calendar.attendee,email:0
msgid "Email of Invited Person"
msgstr "被邀请人的电子邮件"
msgstr "被邀请邮件"
#. module: base_calendar
#: model:crm.meeting.type,name:base_calendar.categ_meet1
@ -894,12 +895,12 @@ msgstr "8月"
#: selection:calendar.todo,week_list:0
#: selection:crm.meeting,week_list:0
msgid "Monday"
msgstr "一"
msgstr "星期一"
#. module: base_calendar
#: model:crm.meeting.type,name:base_calendar.categ_meet4
msgid "Open Discussion"
msgstr "打开讨论板"
msgstr "公开讨论"
#. module: base_calendar
#: model:ir.model,name:base_calendar.model_ir_model
@ -973,7 +974,7 @@ msgstr "11月"
#. module: base_calendar
#: help:calendar.attendee,member:0
msgid "Indicate the groups that the attendee belongs to"
msgstr "与会者属于指定的组"
msgstr "表明与会者属的组"
#. module: base_calendar
#: field:calendar.event,mo:0
@ -1003,7 +1004,7 @@ msgstr "不确定"
#: constraint:calendar.todo:0
#: constraint:crm.meeting:0
msgid "Error ! End date cannot be set before start date."
msgstr "错!结束日期不能设置为早于开始日期。"
msgstr "错!结束日期不能设置为早于开始日期。"
#. module: base_calendar
#: field:calendar.alarm,trigger_occurs:0
@ -1035,7 +1036,7 @@ msgstr "间隔"
#: selection:calendar.todo,week_list:0
#: selection:crm.meeting,week_list:0
msgid "Wednesday"
msgstr "三"
msgstr "星期三"
#. module: base_calendar
#: field:calendar.alarm,name:0
@ -1051,7 +1052,7 @@ msgstr "概要"
#: field:crm.meeting,active:0
#: field:res.alarm,active:0
msgid "Active"
msgstr "效"
msgstr "效"
#. module: base_calendar
#: code:addons/base_calendar/base_calendar.py:399
@ -1062,7 +1063,7 @@ msgstr "你不能复制日程参与者"
#. module: base_calendar
#: view:calendar.event:0
msgid "Choose day in the month where repeat the meeting"
msgstr "选择在一个月里重复会议的日子"
msgstr "选择重复事件每个月内的重复日期"
#. module: base_calendar
#: field:calendar.alarm,action:0
@ -1115,7 +1116,7 @@ msgstr "搜索事件"
msgid ""
"If the active field is set to true, it will allow you to hide the event "
"alarm information without removing it."
msgstr "如果有效字段设为True它将允许你隐藏提醒信息而不需要删除"
msgstr "如果有效字段设为True它将允许你隐藏提醒信息而不需要删除事件"
#. module: base_calendar
#: field:calendar.event,end_type:0
@ -1137,12 +1138,12 @@ msgstr "提醒详情"
#. module: base_calendar
#: model:crm.meeting.type,name:base_calendar.categ_meet3
msgid "Off-site Meeting"
msgstr "离线会议?"
msgstr "离席会议"
#. module: base_calendar
#: view:crm.meeting:0
msgid "Day of Month"
msgstr "该月该日"
msgstr "月内日期"
#. module: base_calendar
#: selection:calendar.alarm,state:0
@ -1239,7 +1240,7 @@ msgstr "循环"
#: selection:calendar.todo,week_list:0
#: selection:crm.meeting,week_list:0
msgid "Thursday"
msgstr "四"
msgstr "星期四"
#. module: base_calendar
#: field:calendar.event,exrule:0
@ -1557,7 +1558,7 @@ msgstr "同意"
#: selection:calendar.todo,week_list:0
#: selection:crm.meeting,week_list:0
msgid "Saturday"
msgstr "六"
msgstr "星期六"
#. module: base_calendar
#: field:calendar.event,interval:0
@ -1571,7 +1572,7 @@ msgstr "逐个重复"
#: selection:calendar.todo,byday:0
#: selection:crm.meeting,byday:0
msgid "Second"
msgstr "第二"
msgstr "第二"
#. module: base_calendar
#: field:calendar.attendee,availability:0
@ -1614,4 +1615,4 @@ msgstr ""
#: selection:calendar.todo,byday:0
#: selection:crm.meeting,byday:0
msgid "Fifth"
msgstr "第五"
msgstr "第五"

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="calendar_event_rule_my" model="ir.rule">
<field name="name">Own events</field>
<field ref="model_calendar_event" name="model_id"/>
<field name="domain_force">[('partner_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<record id="calendar_event_rule_employee" model="ir.rule">
<field ref="model_calendar_event" name="model_id"/>
<field name="name">All Calendar Event for employees</field>
<field name="domain_force">[(1,'=',1)]</field>
<field eval="[(4,ref('base.group_user'))]" name="groups"/>
</record>
<record id="calendar_attendee_rule_my" model="ir.rule">
<field name="name">Own attendees</field>
<field ref="model_calendar_attendee" name="model_id"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
</data>
</openerp>

View File

@ -1,15 +1,17 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_calendar_attendee,calendar.attendee,model_calendar_attendee,,1,1,1,1
access_calendar_attendee_portal,calendar.attendee_portal,model_calendar_attendee,base.group_portal,1,1,0,0
access_calendar_attendee_employee,calendar.attendee_employee,model_calendar_attendee,base.group_user,1,1,1,1
access_calendar_alarm,calendar.alarm,model_calendar_alarm,base.group_user,1,1,1,1
access_calendar_event_manager,calendar.event.manager,model_calendar_event,base.group_sale_manager,1,1,1,1
access_calendar_event,calendar.event,model_calendar_event,base.group_sale_salesman,1,1,1,0
access_calendar_event_all,calendar.event_all,model_calendar_event,base.group_user,1,1,1,1
access_calendar_event_all_user,calendar.event_all_user,model_calendar_event,base.group_portal,1,0,0,0
access_calendar_event_all_employee,calendar.event_all_employee,model_calendar_event,base.group_user,1,1,1,1
access_calendar_event_partner_manager,calendar.event.partner.manager,model_calendar_event,base.group_partner_manager,1,1,1,1
access_calendar_event_type_all,calendar.event.type.all,model_calendar_event_type,,1,0,0,0
access_calendar_event_type_all,calendar.event.type.all,model_calendar_event_type,base.group_user,1,0,0,0
access_calendar_event_type_sale_manager,calendar.event.type.manager,model_calendar_event_type,base.group_sale_manager,1,1,1,0
access_calendar_event_type_sale_user,calendar.event.type.user,model_calendar_event_type,base.group_user,1,0,0,0
access_calendar_event_type_sale_user,calendar.event.type.salesman,model_calendar_event_type,base.group_sale_salesman,1,0,0,0
access_calendar_event_type_manager,calendar.event.type.manager,model_calendar_event_type,base.group_system,1,1,1,1
access_calendar_alarm_manager,access_calendar_alarm_manager,model_calendar_alarm_manager,,1,1,1,1
access_calendar_contacts_all,access_calendar_contacts_all,model_calendar_contacts,,1,1,1,1
access_calendar_alarm_manager,access_calendar_alarm_manager,model_calendar_alarm_manager,base.group_user,1,1,1,1
access_calendar_contacts_all,access_calendar_contacts_all,model_calendar_contacts,base.group_user,1,1,1,1
access_calendar_contacts,access_calendar_contacts,model_calendar_contacts,base.group_system,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_calendar_attendee access_calendar_attendee_portal calendar.attendee calendar.attendee_portal model_calendar_attendee base.group_portal 1 1 1 0 1 0
3 access_calendar_attendee_employee calendar.attendee_employee model_calendar_attendee base.group_user 1 1 1 1
4 access_calendar_alarm calendar.alarm model_calendar_alarm base.group_user 1 1 1 1
5 access_calendar_event_manager calendar.event.manager model_calendar_event base.group_sale_manager 1 1 1 1
6 access_calendar_event calendar.event model_calendar_event base.group_sale_salesman 1 1 1 0
7 access_calendar_event_all access_calendar_event_all_user calendar.event_all calendar.event_all_user model_calendar_event base.group_user base.group_portal 1 1 0 1 0 1 0
8 access_calendar_event_all_employee calendar.event_all_employee model_calendar_event base.group_user 1 1 1 1
9 access_calendar_event_partner_manager calendar.event.partner.manager model_calendar_event base.group_partner_manager 1 1 1 1
10 access_calendar_event_type_all calendar.event.type.all model_calendar_event_type base.group_user 1 0 0 0
11 access_calendar_event_type_sale_manager calendar.event.type.manager model_calendar_event_type base.group_sale_manager 1 1 1 0
12 access_calendar_event_type_sale_user calendar.event.type.user model_calendar_event_type base.group_user 1 0 0 0
13 access_calendar_event_type_sale_user calendar.event.type.salesman model_calendar_event_type base.group_sale_salesman 1 0 0 0
14 access_calendar_event_type_manager calendar.event.type.manager model_calendar_event_type base.group_system 1 1 1 1
15 access_calendar_alarm_manager access_calendar_alarm_manager model_calendar_alarm_manager base.group_user 1 1 1 1
16 access_calendar_contacts_all access_calendar_contacts_all model_calendar_contacts base.group_user 1 1 1 1
17 access_calendar_contacts access_calendar_contacts model_calendar_contacts base.group_system 1 1 1 1

View File

@ -11,7 +11,7 @@
</t>
</t>
<t t-name='notify_title'>
<span t-attf-class="'link2event eid_' + id" class='link2event eid_" + res.event_id + "'>
<span t-attf-class="link2event eid_#{id}">
<t t-esc="title"/>
</span>
</t>

View File

@ -3,8 +3,8 @@
-
!record {model: calendar.event, id: calendar_event_technicalpresentation0}:
class: private
date: '2011-04-30 16:00:00'
date_deadline: '2011-04-30 18:30:00'
start: '2011-04-30 16:00:00'
stop: '2011-04-30 18:30:00'
description: The Technical Presentation will cover following topics:\n* Creating OpenERP
class\n* Views\n* Wizards\n* Workflows
duration: 2.5
@ -14,7 +14,7 @@
Now I will set recurrence for this event to occur monday and friday of week
-
!python {model: calendar.event}: |
data = {'fr': 1, 'mo': 1, 'interval': 1, 'rrule_type': 'weekly', 'end_type': 'end_date', 'end_date': '2011-05-31 00:00:00', 'recurrency' : True}
data = {'fr': 1, 'mo': 1, 'interval': 1, 'rrule_type': 'weekly', 'end_type': 'end_date', 'final_date': '2011-05-31 00:00:00', 'recurrency' : True}
self.write(cr, uid, [ref("calendar_event_technicalpresentation0")], data)
-
In order to check that recurrent events are views successfully in calendar view, I will open calendar view of events
@ -25,18 +25,18 @@
In order to check that recurrent events are views successfully in calendar view, I will search for one of the recurrent event and count the number of events
-
!python {model: calendar.event}: |
ids = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': True} )
ids = self.search(cr, uid, [('start', '>=', '2011-04-30 16:00:00'), ('start', '<=', '2011-05-31 00:00:00')], context={'virtual_id': True} )
assert len(ids) == 9, 'Wrong number of events found'
-
Now I move a virtual event, to see that a real event is well created and depending from the native recurrence
-
!python {model: calendar.event}: |
ids = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': True} )
before = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': False})
ids = self.search(cr, uid, [('start', '>=', '2011-04-30 16:00:00'), ('start', '<=', '2011-05-31 00:00:00')], context={'virtual_id': True} )
before = self.search(cr, uid, [('start', '>=', '2011-04-30 16:00:00'), ('start', '<=', '2011-05-31 00:00:00')], context={'virtual_id': False})
# We start by detach the event
newid = self._detach_one_event(cr, uid,ids[1])
self.write(cr, uid,[newid], {'name':'New Name','recurrency' : True}, context={'virtual_id': True})
after = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': False})
after = self.search(cr, uid, [('start', '>=', '2011-04-30 16:00:00'), ('start', '<=', '2011-05-31 00:00:00')], context={'virtual_id': False})
assert len(after) == len(before)+1, 'Wrong number of events found, after to have moved a virtual event'
new_id = list(set(after)-set(before))[0]
new_event = self.browse(cr,uid,new_id,context=context)
@ -47,8 +47,8 @@
!record {model: calendar.event, id: calendar_event_alldaytestevent0}:
allday: 1
class: confidential
date: '2011-04-30 00:00:00'
date_deadline: '2011-04-30 00:00:00'
start: '2011-04-30 00:00:00'
stop: '2011-04-30 00:00:00'
description: 'All day technical test '
location: School
name: All day test event
@ -70,7 +70,8 @@
-
!record {model: calendar.event, id: calendar.event_sprintreview1}:
name: Begin of month meeting
date: !eval time.strftime('%Y-%m-%d 12:00:00')
start: !eval time.strftime('%Y-%m-%d 12:00:00')
stop: !eval time.strftime('%Y-%m-%d 18:00:00')
recurrency: true
rrule: FREQ=MONTHLY;INTERVAL=1;COUNT=12;BYDAY=1MO
-

View File

@ -5,8 +5,8 @@
-
!record {model: calendar.event, id: calendar_event_testmeeting0}:
count: 5
date: '2011-04-13 11:04:00'
date_deadline: '2011-04-13 12:04:00'
start: '2011-04-13 11:04:00'
stop: '2011-04-13 12:04:00'
day: 0.0
duration: 1.0
name: Test Meeting
@ -17,17 +17,17 @@
I search for all the recurrent meetings.
-
!python {model: calendar.event}: |
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('calendar_event_testmeeting0')]),('date','>=','2011-03-13'), ('date_deadline', '<=', '2011-05-13')], context={'virtual_id': True})
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('calendar_event_testmeeting0')]),('start','>=','2011-03-13'), ('stop', '<=', '2011-05-13')], context={'virtual_id': True})
assert len(meeting_ids) == 5, 'Recurrent daily meetings are not created !'
-
I create a weekly meeting till a particular end date.
-
!record {model: calendar.event, id: calendar_event_reviewcodewithprogrammer0}:
date: '2011-04-18 11:47:00'
date_deadline: '2011-04-18 12:47:00'
start: '2011-04-18 11:47:00'
stop: '2011-04-18 12:47:00'
day: 0.0
duration: 1.0
end_date: '2011-04-30'
final_date: '2011-04-30'
end_type: end_date
fr: true
mo: true
@ -36,21 +36,21 @@
we: true
name: Review code with programmer
recurrency: true
recurrent_id: 0.0
recurrent_id: 0
rrule_type: weekly
-
I search for all the recurrent weekly meetings.
-
!python {model: calendar.event}: |
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('calendar_event_reviewcodewithprogrammer0')]),('date','>=','2011-03-13'), ('date_deadline', '<=', '2011-05-13')], context={'virtual_id': True})
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('calendar_event_reviewcodewithprogrammer0')]),('start','>=','2011-03-13'), ('stop', '<=', '2011-05-13')], context={'virtual_id': True})
assert len(meeting_ids) == 10, 'Recurrent weekly meetings are not created !'
-
I want to schedule a meeting every month for Sprint review.
-
!record {model: calendar.event, id: calendar_event_sprintreview0}:
count: 12
date: '2011-04-01 12:01:00'
date_deadline: '2011-04-01 13:01:00'
start: '2011-04-01 12:01:00'
stop: '2011-04-01 13:01:00'
day: 1
duration: 1.0
name: Sprint Review
@ -61,7 +61,7 @@
I search for all the recurrent monthly meetings.
-
!python {model: calendar.event}: |
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('calendar_event_sprintreview0')]),('date','>=','2011-03-01'), ('date_deadline', '<=', '2012-05-13')], context={'virtual_id': True})
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('calendar_event_sprintreview0')]),('start','>=','2011-03-01'), ('stop', '<=', '2012-05-13')], context={'virtual_id': True})
assert len(meeting_ids) == 12, 'Recurrent monthly meetings are not created !'
-
I change name of my monthly Sprint Review meeting.
@ -73,7 +73,7 @@
I check whether all the records are edited or not.
-
!python {model: calendar.event}: |
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('calendar_event_sprintreview0')]),('date','>=','2011-03-01'), ('date_deadline', '<=', '2012-05-13')], context={'virtual_id': True})
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('calendar_event_sprintreview0')]),('start','>=','2011-03-01'), ('stop', '<=', '2012-05-13')], context={'virtual_id': True})
meetings = self.browse(cr, uid, meeting_ids, context)
for meeting in meetings:
assert meeting.name == 'Sprint Review for google modules', 'Name not changed for id: %s' %meeting.id

View File

@ -9,7 +9,6 @@
<field name="context">{'default_ref': 'stock.picking,'+str(context.get('active_id', False))}</field>
<field name="domain">[('ref','=','stock.picking,'+str(context.get('active_id',False)))]</field>
</record>
<record id="crm_claim_from_delivery" model="ir.ui.view">
<field name="name">crm.claim.from_delivery.form</field>
<field name="model">stock.picking</field>
@ -17,11 +16,13 @@
<field name="arch" type="xml">
<xpath expr="/form/sheet/h1" position="before">
<div class="oe_right oe_button_box">
<button name="%(action_claim_from_delivery)d" string="Claims" type="action" attrs="{'invisible': [('picking_type_id.code', '!=', 'outgoing')]}"/>
<button class="oe_inline oe_stat_button" type="action" attrs="{'invisible': [('picking_type_id.code', '!=', 'outgoing')]}"
name="%(action_claim_from_delivery)d" icon="fa-comments" >
<field string="Claims" name="claim_count" widget="statinfo"/>
</button>
</div>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -36,21 +36,7 @@ class calendar_event(osv.Model):
res = super(calendar_event, self).create(cr, uid, vals, context=context)
obj = self.browse(cr, uid, res, context=context)
if obj.opportunity_id:
self.pool.get('crm.lead').log_meeting(cr, uid, [obj.opportunity_id.id], obj.name, obj.date, obj.duration, context=context)
self.pool.get('crm.lead').log_meeting(cr, uid, [obj.opportunity_id.id], obj.name, obj.start, obj.duration, context=context)
return res
class calendar_attendee(osv.osv):
""" Calendar Attendee """
_inherit = 'calendar.attendee'
_description = 'Calendar Attendee'
def _noop(self, cr, uid, ids, name, arg, context=None):
return dict.fromkeys(ids, False)
_columns = {
'categ_id': fields.function(_noop, string='Event Type', deprecated="Unused Field - TODO : Remove it in trunk", type="many2one", relation="crm.case.categ"),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -215,7 +215,12 @@ class crm_lead(format_address, osv.osv):
duration = len(no_days)
res[lead.id][field] = abs(int(duration))
return res
def _meeting_count(self, cr, uid, ids, field_name, arg, context=None):
Event = self.pool['calendar.event']
return {
opp_id: Event.search_count(cr,uid, [('opportunity_id', '=', opp_id)], context=context)
for opp_id in ids
}
_columns = {
'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', track_visibility='onchange',
select=True, help="Linked partner (optional). Usually created when converting the lead."),
@ -290,6 +295,7 @@ class crm_lead(format_address, osv.osv):
'payment_mode': fields.many2one('crm.payment.mode', 'Payment Mode', \
domain="[('section_id','=',section_id)]"),
'planned_cost': fields.float('Planned Costs'),
'meeting_count': fields.function(_meeting_count, string='# Meetings', type='integer'),
}
_defaults = {

View File

@ -14,6 +14,7 @@
<record model="crm.case.stage" id="stage_lead2">
<field name="name">Dead</field>
<field name="case_default">1</field>
<field name="fold">1</field>
<field name="probability">0</field>
<field name="on_change">1</field>
<field name="sequence">30</field>

View File

@ -103,9 +103,11 @@
</header>
<sheet>
<div class="oe_right oe_button_box" name="buttons">
<button class="oe_inline" type="action" string="Calls"
name="%(crm.crm_case_categ_phone_incoming0)d"
context="{'default_opportunity_id': active_id, 'search_default_opportunity_id': active_id, 'default_partner_id': partner_id, 'default_duration': 1.0}"/>
<button class="oe_inline oe_stat_button" type="action"
context="{'default_opportunity_id': active_id, 'search_default_opportunity_id': active_id, 'default_partner_id': partner_id, 'default_duration': 1.0}"
name="%(crm.crm_case_categ_phone_incoming0)d" icon="fa-phone">
<div>Schedule/Log<br/>Calls</div>
</button>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
@ -389,11 +391,16 @@
</header>
<sheet>
<div class="oe_right oe_button_box">
<button class="oe_inline" type="action" string="Calls"
name="%(crm.crm_case_categ_phone_incoming0)d"
context="{'default_opportunity_id': active_id, 'search_default_opportunity_id': active_id, 'default_partner_id': partner_id, 'default_duration': 1.0}"/>
<button class="oe_inline" string="Schedule a Meeting" type="object"
name="action_schedule_meeting" context="{'partner_id': partner_id}"/>
<button class="oe_inline oe_stat_button" type="action"
name="%(crm.crm_case_categ_phone_incoming0)d" icon="fa-phone"
context="{'default_opportunity_id': active_id, 'search_default_opportunity_id': active_id, 'default_partner_id': partner_id, 'default_duration': 1.0}">
<div>Schedule/Log<br/>Calls</div>
</button>
<button class="oe_inline oe_stat_button" type="object"
context="{'partner_id': partner_id}"
name="action_schedule_meeting" icon="fa-calendar">
<field string="Meetings" name="meeting_count" widget="statinfo"/>
</button>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>

View File

@ -31,6 +31,7 @@ class crm_phonecall(osv.osv):
_description = "Phonecall"
_order = "id desc"
_inherit = ['mail.thread']
_columns = {
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),

View File

@ -64,15 +64,20 @@
<field name="state" nolabel="1" widget="statusbar" clickable="True"/>
</header>
<sheet string="Phone Call">
<div class="oe_right">
<button string="Convert to Opportunity"
type="object"
name="action_button_convert2opportunity"
attrs="{'invisible':[ '|', ('opportunity_id','!=',False), ('state', 'not in', ('open', 'done'))]}"/>
<button string="Schedule Other Call"
name="%(phonecall_to_phonecall_act)d"
type="action"/>
<button string="Schedule a Meeting" name="action_make_meeting" type="object"/>
<div class="oe_right oe_button_box">
<button class="oe_inline oe_stat_button" type="object" name="action_button_convert2opportunity"
icon="fa-star" attrs="{'invisible':[ '|', ('opportunity_id','!=',False), ('state', 'not in', ('open', 'done'))]}"
help="Convert To Opportunity">
<div>Convert To<br/>Opportunity</div>
</button>
<button class="oe_inline oe_stat_button" type="action"
name="%(phonecall_to_phonecall_act)d" icon="fa-phone">
<div>Schedule<br/>Other Call</div>
</button>
<button class="oe_inline oe_stat_button" name="action_make_meeting" type="object" icon="fa-calendar fa-fw">
<div>Schedule<br/>A Meeting</div>
</button>
</div>
<div class="oe_title">
<div class="oe_edit_only">

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
"PO-Revision-Date: 2012-12-14 10:12+0000\n"
"Last-Translator: Rui Franco (multibase.pt) <Unknown>\n"
"PO-Revision-Date: 2014-05-06 17:09+0000\n"
"Last-Translator: Daniel Reis <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 06:22+0000\n"
"X-Generator: Launchpad (build 16985)\n"
"X-Launchpad-Export-Date: 2014-05-07 06:26+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: crm
#: view:crm.lead.report:0
@ -89,7 +89,7 @@ msgstr "Tempo para fechar"
#: view:crm.case.stage:0
#: field:crm.case.stage,name:0
msgid "Stage Name"
msgstr "Nome do Estágio"
msgstr "Nome da Etapa"
#. module: crm
#: view:crm.lead:0
@ -269,6 +269,8 @@ msgstr "Análise de Prospectos"
#, python-format
msgid "<b>%s a call</b> for the <em>%s</em>."
msgstr ""
"Copy text \t\r\n"
"<b>%s uma chamada</b> para <em>%s</em>"
#. module: crm
#: model:ir.actions.act_window,name:crm.crm_case_resource_type_act
@ -354,6 +356,7 @@ msgstr "Contato"
msgid ""
"When escalating to this team override the salesman with the team leader."
msgstr ""
"Ao escalar para esta equipa, colocar o chefe de equipa como vendedor."
#. module: crm
#: model:process.transition,name:crm.process_transition_opportunitymeeting0
@ -543,7 +546,7 @@ msgstr "Mailings"
#. module: crm
#: model:mail.message.subtype,description:crm.mt_lead_stage
msgid "Stage changed"
msgstr "Estado alterado"
msgstr "Etapa mudou"
#. module: crm
#: selection:crm.lead.report,creation_month:0
@ -857,6 +860,8 @@ msgid ""
"Link between stages and sales teams. When set, this limitate the current "
"stage to the selected sales teams."
msgstr ""
"Ligação entre as etapas e as equipas de vendas. Ao definir, a etapa atual só "
"se aplica à equipa de vendas selecionada."
#. module: crm
#: view:crm.case.stage:0
@ -1106,7 +1111,7 @@ msgstr "Ano da Chamada"
#: view:crm.lead.report:0
#: field:crm.lead.report,stage_id:0
msgid "Stage"
msgstr "Estágio"
msgstr "Etapa"
#. module: crm
#: view:crm.phonecall.report:0
@ -1131,7 +1136,7 @@ msgstr "As chamadas telefônicas que estão em estado pendente"
#: model:ir.actions.act_window,name:crm.crm_lead_stage_act
#: model:ir.ui.menu,name:crm.menu_crm_lead_stage_act
msgid "Stages"
msgstr "Estágios"
msgstr "Etapas"
#. module: crm
#: help:sale.config.settings,module_crm_helpdesk:0
@ -1191,8 +1196,8 @@ msgid ""
"Setting this stage will change the probability automatically on the "
"opportunity."
msgstr ""
"A definição dessa fase vai mudar a probabilidade da oportunidade "
"automaticamente."
"Passar para esta Etapa irá vai mudar automaticamente a probabilidade da "
"oportunidade."
#. module: crm
#: view:crm.lead:0
@ -1249,9 +1254,8 @@ msgid ""
"This field is used to distinguish stages related to Leads from stages "
"related to Opportunities, or to specify stages available for both types."
msgstr ""
"Este campo é usado para distinguir estágios dos Prospectos dos estágios das "
"Oportunidades, ou para especificar estágios disponíveis simultaneamente para "
"Prospecto e Oportunidades."
"Este campo é usado para distinguir as etapas dos Prospectos das etapas das "
"Oportunidades, e as etapas que podem ser comuns a ambos."
#. module: crm
#: model:mail.message.subtype,name:crm.mt_lead_create
@ -1374,7 +1378,7 @@ msgstr "Descrição da Segmentação"
#. module: crm
#: view:crm.lead:0
msgid "Lead Description"
msgstr ""
msgstr "Descrição do Prospecto"
#. module: crm
#: code:addons/crm/crm_lead.py:578
@ -1430,7 +1434,7 @@ msgstr "Cancelar"
#. module: crm
#: view:crm.lead:0
msgid "Opportunities Assigned to Me or My Team(s)"
msgstr ""
msgstr "Oportunidades atribuídas a mim ou à minha equipa"
#. module: crm
#: model:crm.case.categ,name:crm.categ_oppor4
@ -1479,7 +1483,7 @@ msgstr "Fundir prospectos/oportunidades"
#. module: crm
#: help:crm.case.stage,sequence:0
msgid "Used to order stages. Lower is better."
msgstr ""
msgstr "Usado para ordenar etapas. Menor surge primeiro."
#. module: crm
#: model:ir.actions.act_window,name:crm.crm_phonecall_categ_action
@ -1499,7 +1503,7 @@ msgstr "Utilizadores"
#. module: crm
#: model:mail.message.subtype,name:crm.mt_lead_stage
msgid "Stage Changed"
msgstr "Estágio Alterado"
msgstr "Etapa mudou"
#. module: crm
#: field:crm.case.stage,section_ids:0
@ -1649,10 +1653,9 @@ msgid ""
"stage. For example, if a stage is related to the status 'Close', when your "
"document reaches this stage, it is automatically closed."
msgstr ""
"O estado do seu documento irá mudar automaticamente em função do estágio "
"seleccionado. Por exemplo, se um estágio está relacionado com o estado "
"'Fechado', quando o seu documento alcançar este estágio, o mesmo será "
"automaticamente fechado."
"O estado do seu documento irá mudar automaticamente em função da etapda "
"selecionada. Por exemplo, se a uma Etapa corresponde o Estado 'Fechado', "
"quando o seu documento alcançar essa Etapa será automaticamente fechado."
#. module: crm
#: view:crm.lead2opportunity.partner.mass:0
@ -1730,12 +1733,12 @@ msgstr "Responder Para"
#. module: crm
#: view:crm.lead:0
msgid "Display"
msgstr ""
msgstr "Mostrar"
#. module: crm
#: view:board.board:0
msgid "Opportunities by Stage"
msgstr "Oportunidades por estágio"
msgstr "Oportunidades por Etapa"
#. module: crm
#: model:process.transition,note:crm.process_transition_leadpartner0
@ -1781,7 +1784,7 @@ msgstr "Google Adwords"
#. module: crm
#: view:crm.case.section:0
msgid "Select Stages for this Sales Team"
msgstr "Seleccionar estágios desta Equipa de Vendas"
msgstr "Selecionar etapas da Equipa de Vendas"
#. module: crm
#: view:crm.lead:0
@ -1848,7 +1851,7 @@ msgstr "Televendas"
#. module: crm
#: view:crm.lead:0
msgid "Leads Assigned to Me or My Team(s)"
msgstr ""
msgstr "Prospectos atribuídos a mim ou à minha equipa"
#. module: crm
#: model:ir.model,name:crm.model_crm_segmentation_line
@ -1909,7 +1912,7 @@ msgstr "Equipas de vendas"
#. module: crm
#: field:crm.case.stage,case_default:0
msgid "Default to New Sales Team"
msgstr ""
msgstr "Predefinido para novas equipas"
#. module: crm
#: view:crm.lead:0
@ -2292,8 +2295,8 @@ msgid ""
"This stage is not visible, for example in status bar or kanban view, when "
"there are no records in that stage to display."
msgstr ""
"Este estágio não é visível, por exemplo na barra de estado ou na vista "
"kanban, quando não existirem registos a exibir naquele estado."
"Esta Etapa não é visível, por exemplo na barra de estado ou na vista kanban, "
"quando não existirem registos a exibir nessa Etapa."
#. module: crm
#: field:crm.lead.report,nbr:0
@ -2364,7 +2367,7 @@ msgstr "Chamadas telefónicas atribuídas a mim ou minha(s) equipa(s)"
#. module: crm
#: view:crm.lead:0
msgid "Reset"
msgstr ""
msgstr "Reiniciar"
#. module: crm
#: view:sale.config.settings:0
@ -2419,7 +2422,7 @@ msgstr ""
#. module: crm
#: field:crm.case.stage,fold:0
msgid "Fold by Default"
msgstr ""
msgstr "Ver pregado"
#. module: crm
#: field:crm.case.stage,state:0
@ -2460,7 +2463,7 @@ msgstr ""
#. module: crm
#: model:ir.actions.act_window,name:crm.act_oppor_stage_user
msgid "Planned Revenue By User and Stage"
msgstr "Rendimento planeado pelo utilizador e fase"
msgstr "Vendas previstas por utilizador e etapa"
#. module: crm
#: view:crm.phonecall:0
@ -2737,7 +2740,7 @@ msgstr "Chamadas Registadas"
#: model:mail.message.subtype,name:crm.mt_lead_won
#: model:mail.message.subtype,name:crm.mt_salesteam_lead_won
msgid "Opportunity Won"
msgstr ""
msgstr "Oportunidade ganha"
#. module: crm
#: model:ir.actions.act_window,name:crm.crm_case_section_act_tree
@ -2760,7 +2763,7 @@ msgstr "Categoria do caso"
#. module: crm
#: view:board.board:0
msgid "Planned Revenue by Stage and User"
msgstr "Rendimento planeado por utilizador e estágio"
msgstr "Vendas previstas por utilizador e etapa"
#. module: crm
#: selection:crm.lead,priority:0
@ -2802,7 +2805,7 @@ msgstr "Novembro"
#: view:crm.lead.report:0
#: model:ir.actions.act_window,name:crm.act_opportunity_stage
msgid "Opportunities By Stage"
msgstr "Oportunidade por estágio"
msgstr "Oportunidades por etapa"
#. module: crm
#: selection:crm.lead.report,creation_month:0
@ -2844,7 +2847,7 @@ msgstr "Ano de encerramento esperado"
#. module: crm
#: model:ir.model,name:crm.model_crm_case_stage
msgid "Stage of case"
msgstr "Estágio do caso"
msgstr "Etapa do caso"
#. module: crm
#: selection:crm.opportunity2phonecall,action:0
@ -2886,7 +2889,7 @@ msgstr "Criar Data"
#. module: crm
#: view:crm.lead:0
msgid "at"
msgstr ""
msgstr "em"
#. module: crm
#: model:crm.case.stage,name:crm.stage_lead1
@ -3112,7 +3115,7 @@ msgstr "Notícias"
#. module: crm
#: model:mail.message.subtype,name:crm.mt_salesteam_lead_stage
msgid "Opportunity Stage Changed"
msgstr ""
msgstr "Etapa da oportunidade mudou"
#. module: crm
#: model:ir.actions.act_window,help:crm.crm_lead_stage_act
@ -3126,6 +3129,13 @@ msgid ""
" </p>\n"
" "
msgstr ""
"<p class=\"oe_view_nocontent_create\">\n"
"Clique para definir uma nova etapa no seu funil de vendas.\n"
"</p><p>\n"
"As etapas permitem os comerciais perceber em que ponto do ciclo de venda "
"está cada oportunidade.\n"
"</p>\n"
" "
#~ msgid "State of Mind Computation"
#~ msgstr "Processamento do estado da mente"

View File

@ -25,7 +25,7 @@ class res_partner(osv.osv):
""" Inherits partner and adds CRM information in the partner form """
_inherit = 'res.partner'
def _opportunity_meeting_count(self, cr, uid, ids, field_name, arg, context=None):
def _opportunity_meeting_phonecall_count(self, cr, uid, ids, field_name, arg, context=None):
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:
@ -36,18 +36,21 @@ class res_partner(osv.osv):
}
except:
pass
for partner in self.browse(cr, uid, ids, context):
res[partner.id]['phonecall_count'] = len(partner.phonecall_ids)
return res
_columns = {
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
'opportunity_ids': fields.one2many('crm.lead', 'partner_id',\
'Leads and Opportunities', domain=[('probability', 'not in', ['0', '100'])]),
'meeting_ids': fields.many2many('calendar.event', 'calendar_event_partner_rel','partner_id', 'meeting_id',
'meeting_ids': fields.many2many('calendar.event', 'calendar_event_res_partner_rel','res_partner_id', 'calendar_event_id',
'Meetings'),
'phonecall_ids': fields.one2many('crm.phonecall', 'partner_id',\
'Phonecalls'),
'opportunity_count': fields.function(_opportunity_meeting_count, string="Opportunity", type='integer', multi='opp_meet'),
'meeting_count': fields.function(_opportunity_meeting_count, string="# Meetings", type='integer', multi='opp_meet'),
'opportunity_count': fields.function(_opportunity_meeting_phonecall_count, string="Opportunity", type='integer', multi='opp_meet'),
'meeting_count': fields.function(_opportunity_meeting_phonecall_count, string="# Meetings", type='integer', multi='opp_meet'),
'phonecall_count': fields.function(_opportunity_meeting_phonecall_count, string="Phonecalls", type="integer", multi='opp_meet'),
}
def copy(self, cr, uid, record_id, default=None, context=None):

View File

@ -90,15 +90,25 @@
<field name="section_id" completion="1" groups="base.group_multi_salesteams"/>
</field>
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline" type="action" string="Opportunities"
<button class="oe_inline oe_stat_button" type="action"
attrs="{'invisible': [('customer', '=', False)]}"
name="%(crm.crm_case_category_act_oppor11)d"
context="{'search_default_partner_id': active_id}"/>
<button class="oe_inline" string="Schedule a Meeting" type="object"
name="schedule_meeting" context="{'partner_id': active_id}"/>
<button class="oe_inline" type="action" string="Calls"
icon="fa-star"
context="{'search_default_partner_id': active_id}">
<field string="Opportunities" name="opportunity_count" widget="statinfo"/>
</button>
<button class="oe_inline oe_stat_button" type="object"
name="schedule_meeting"
icon="fa-calendar"
context="{'partner_id': active_id}">
<field string="Meetings" name="meeting_count" widget="statinfo"/>
</button>
<button class="oe_inline oe_stat_button" type="action"
name="%(crm.crm_case_categ_phone_incoming0)d"
context="{'search_default_partner_id': active_id, 'default_duration': 1.0}"/>
icon="fa-phone"
context="{'search_default_partner_id': active_id, 'default_duration': 1.0}">
<field string="Calls" name="phonecall_count" widget="statinfo"/>
</button>
</xpath>
</data>
</field>

View File

@ -22,8 +22,6 @@ access_crm_phonecall_report_user,crm.phonecall.report.user,model_crm_phonecall_r
access_crm_phonecall_report_manager,crm.phonecall.report,model_crm_phonecall_report,base.group_sale_manager,1,1,1,1
access_res_partner_manager,res.partner.crm.manager,base.model_res_partner,base.group_sale_manager,1,0,0,0
access_res_partner_category_manager,res.partner.category.crm.manager,base.model_res_partner_category,base.group_sale_manager,1,0,0,0
access_calendar_attendee_crm_user,calendar.attendee.crm.user,model_calendar_attendee,base.group_sale_salesman,1,1,1,0
access_calendar_attendee_crm_manager,calendar.attendee.crm.manager,model_calendar_attendee,base.group_sale_manager,1,1,1,1
access_res_partner,res.partner.crm.user,base.model_res_partner,base.group_sale_salesman,1,1,1,0
access_res_partner_category,res.partner.category.crm.user,base.model_res_partner_category,base.group_sale_salesman,1,1,1,0
mail_mailgate_thread,mail.thread,mail.model_mail_thread,base.group_sale_salesman,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
22 access_crm_phonecall_report_manager crm.phonecall.report model_crm_phonecall_report base.group_sale_manager 1 1 1 1
23 access_res_partner_manager res.partner.crm.manager base.model_res_partner base.group_sale_manager 1 0 0 0
24 access_res_partner_category_manager res.partner.category.crm.manager base.model_res_partner_category base.group_sale_manager 1 0 0 0
access_calendar_attendee_crm_user calendar.attendee.crm.user model_calendar_attendee base.group_sale_salesman 1 1 1 0
access_calendar_attendee_crm_manager calendar.attendee.crm.manager model_calendar_attendee base.group_sale_manager 1 1 1 1
25 access_res_partner res.partner.crm.user base.model_res_partner base.group_sale_salesman 1 1 1 0
26 access_res_partner_category res.partner.category.crm.user base.model_res_partner_category base.group_sale_salesman 1 1 1 0
27 mail_mailgate_thread mail.thread mail.model_mail_thread base.group_sale_salesman 1 1 1 1

View File

@ -88,7 +88,7 @@ Find duplicates, merge leads and assign them to the right salesperson in one ope
<p class="oe_mt32">
Get your opportunities organized to stay focused on the best deals. Manage all your customer interactions from the opportunity like emails, phone calls, internal notes, meetings and quotations.
</p><p>
Follow opportunities that interrests you to get notified upon specific events: deal won or lost, stage changed, new customer demand, etc.
Follow opportunities that interest you to get notified upon specific events: deal won or lost, stage changed, new customer demand, etc.
</p>
</div>
</div>

View File

@ -190,8 +190,15 @@ class crm_claim(osv.osv):
class res_partner(osv.osv):
_inherit = 'res.partner'
def _claim_count(self, cr, uid, ids, field_name, arg, context=None):
Claim = self.pool['crm.claim']
return {
partner_id: Claim.search_count(cr,uid, [('partner_id', '=', partner_id)], context=context)
for partner_id in ids
}
_columns = {
'claims_ids': fields.one2many('crm.claim', 'partner_id', 'Claims'),
'claim_count': fields.function(_claim_count, string='# Claims', type='integer'),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -9,11 +9,11 @@
<field name="priority" eval="50"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline" type="action"
name="%(crm_case_categ_claim0)d"
string="Claims"
<button class="oe_inline oe_stat_button" type="action"
context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}"
groups="base.group_sale_salesman" />
name="%(crm_case_categ_claim0)d" icon="fa-comments" groups="base.group_sale_salesman">
<field string="Claims" name="claim_count" widget="statinfo"/>
</button>
</xpath>
</field>
</record>

View File

@ -35,8 +35,11 @@
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_form_view_leads" />
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']/button" position="after">
<button name="%(convert_lead2projectissue_wizard_action)d" string="Convert to Issue" type="action" help="Convert to Issue" />
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" type="action" name="%(convert_lead2projectissue_wizard_action)d" icon="fa-bug"
help="Convert to Issue">
<div>Convert To<br/>Issue</div>
</button>
</xpath>
</field>
</record>

View File

@ -1,4 +1,3 @@
import simplejson
import werkzeug.urls
import openerp
@ -10,21 +9,12 @@ class EDI(openerp.http.Controller):
def import_url(self, url):
# http://hostname:8069/edi/import_url?url=URIEncodedURL
req = openerp.http.request
modules = webmain.module_boot(req) + ['edi']
modules_str = ','.join(modules)
modules_json = simplejson.dumps(modules)
css = '<link rel="stylesheet" href="/web/css/web.assets_backend"/>'
js = '<script type="text/javascript" src="/web/js/web.assets_backend"></script>'
# `url` may contain a full URL with a valid query string, we basically want to watch out for XML brackets and double-quotes
safe_url = werkzeug.url_quote_plus(url,':/?&;=')
return webmain.html_template % {
'js': js,
'css': css,
'modules': modules_json,
'init': 's.edi.edi_import("%s");' % safe_url,
}
values = dict(init='s.edi.edi_import("%s");' % safe_url)
return req.render('web.webclient_bootstrap', values)
@openerp.http.route('/edi/import_edi_url', type='json', auth='none')
def import_edi_url(self, url):

View File

@ -16,13 +16,18 @@
</div>
<div class="oe_right oe_button_box" name="buttons">
<field name="ref_ir_act_window" invisible="1"/>
<button name="create_action" string="Add context action" type="object"
attrs="{'invisible':[('ref_ir_act_window','!=',False)]}"
help="Display an option on related documents to open a composition wizard with this template"/>
<button name="unlink_action" string="Remove context action" type="object"
<button class="oe_inline oe_stat_button" name="create_action" type="object"
attrs="{'invisible':[('ref_ir_act_window','!=',False)]}" icon="fa-plus"
help="Display an option on related documents to open a composition wizard with this template">
<div>Add<br/>Context Action</div>
</button>
<button name="unlink_action" type="object"
class="oe_stat_button" icon="fa-minus"
attrs="{'invisible':[('ref_ir_act_window','=',False)]}"
help="Remove the contextual action to use this template on related documents"/>
<button name="%(wizard_email_template_preview)d" string="Preview"
help="Remove the contextual action to use this template on related documents" widget="statinfo">
<div>Remove<br/>Context Action</div>
</button>
<button class="oe_inline oe_stat_button" name="%(wizard_email_template_preview)d" icon="fa-search-plus" string="Preview"
type="action" target="new"
context="{'template_id':active_id}"/>
</div>

View File

@ -149,6 +149,12 @@ class event_event(osv.osv):
res[event.id]= True
continue
return res
def _count_registrations(self, cr, uid, ids, field_name, arg, context=None):
return {
event.id: len(event.registration_ids)
for event in self.browse(cr, uid, ids, context=context)
}
_columns = {
'name': fields.char('Event Name', size=64, required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
@ -191,6 +197,7 @@ class event_event(osv.osv):
'company_id': fields.many2one('res.company', 'Company', required=False, change_default=True, readonly=False, states={'done': [('readonly', True)]}),
'is_subscribed' : fields.function(_subscribe_fnc, type="boolean", string='Subscribed'),
'organizer_id': fields.many2one('res.partner', "Organizer"),
'count_registrations': fields.function(_count_registrations, type="integer", string="Registrations"),
}
_defaults = {
'state': 'draft',

View File

@ -65,13 +65,19 @@
<field name="state" widget="statusbar" statusbar_visible="draft,confirm,done"/>
</header>
<sheet>
<div class="oe_right oe_button_box" groups="base.group_user">
<button name="%(event.act_event_list_register_event)d" type="action" string="Registrations" help="Register with this event"/>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
</div>
<div class="oe_right oe_button_box" groups="base.group_user">
<button name="%(event.act_event_list_register_event)d"
type="action"
class="oe_stat_button oe_inline"
icon="fa-github"
help="Register with this event">
<field name="count_registrations" widget="statinfo" string="Registrations"/>
</button>
</div>
<group name="main_field_group">
<group>
<field name="address_id" context="{'show_address': 1}" options='{"always_reload": True}'/>

View File

@ -37,6 +37,9 @@ class product_template(osv.osv):
class product(osv.osv):
_inherit = 'product.product'
_columns = {
'event_ticket_ids': fields.one2many('event.event.ticket', 'product_id', 'Event Tickets'),
}
def onchange_event_ok(self, cr, uid, ids, type, event_ok, context=None):
# cannot directly forward to product.template as the ids are theoretically different

View File

@ -313,6 +313,23 @@ class fleet_vehicle(osv.Model):
except ValueError:
model_id = False
return model_id
def _count_all(self, cr, uid, ids, field_name, arg, context=None):
Odometer = self.pool['fleet.vehicle.odometer']
LogFuel = self.pool['fleet.vehicle.log.fuel']
LogService = self.pool['fleet.vehicle.log.services']
LogContract = self.pool['fleet.vehicle.log.contract']
Cost = self.pool['fleet.vehicle.cost']
return {
vehicle_id: {
'odometer_count': Odometer.search_count(cr, uid, [('vehicle_id', '=', vehicle_id)], context=context),
'fuel_logs_count': LogFuel.search_count(cr, uid, [('vehicle_id', '=', vehicle_id)], context=context),
'service_count': LogService.search_count(cr, uid, [('vehicle_id', '=', vehicle_id)], context=context),
'contract_count': LogContract.search_count(cr, uid, [('vehicle_id', '=', vehicle_id)], context=context),
'cost_count': Cost.search_count(cr, uid, [('vehicle_id', '=', vehicle_id), ('parent_id', '=', False)], context=context)
}
for vehicle_id in ids
}
_name = 'fleet.vehicle'
_description = 'Information on a vehicle'
@ -327,6 +344,11 @@ class fleet_vehicle(osv.Model):
'log_fuel': fields.one2many('fleet.vehicle.log.fuel', 'vehicle_id', 'Fuel Logs'),
'log_services': fields.one2many('fleet.vehicle.log.services', 'vehicle_id', 'Services Logs'),
'log_contracts': fields.one2many('fleet.vehicle.log.contract', 'vehicle_id', 'Contracts'),
'cost_count': fields.function(_count_all, type='integer', string="Costs" , multi=True),
'contract_count': fields.function(_count_all, type='integer', string='Contracts', multi=True),
'service_count': fields.function(_count_all, type='integer', string='Services', multi=True),
'fuel_logs_count': fields.function(_count_all, type='integer', string='Fuel Logs', multi=True),
'odometer_count': fields.function(_count_all, type='integer', string='Odometer', multi=True),
'acquisition_date': fields.date('Acquisition Date', required=False, help='Date when the vehicle has been bought'),
'color': fields.char('Color', size=32, help='Color of the vehicle'),
'state_id': fields.many2one('fleet.vehicle.state', 'State', help='Current state of the vehicle', ondelete="set null"),

View File

@ -211,11 +211,46 @@
<field name="tag_ids" widget="many2many_tags" />
</div>
<div class="oe_right oe_button_box">
<button name="return_action_to_open" type="object" context="{'xml_id':'fleet_vehicle_log_contract_act'}" string="Contracts" help="show the contract for this vehicle" />
<button name="act_show_log_cost" type="object" string="Costs" help="show all the costs for this vehicle" groups="fleet.group_fleet_manager"/>
<button name="return_action_to_open" type="object" context="{'xml_id':'fleet_vehicle_log_services_act'}" string="Services" help="show the services logs for this vehicle" />
<button name="return_action_to_open" type="object" context="{'xml_id':'fleet_vehicle_log_fuel_act'}" string="Fuel Logs" help="show the fuel logs for this vehicle" />
<button name="return_action_to_open" type="object" context="{'xml_id':'fleet_vehicle_odometer_act'}" string="Odometer Logs" help="show the odometer logs for this vehicle" />
<button name="return_action_to_open"
type="object"
class="oe_stat_button"
icon="fa-book"
context="{'xml_id':'fleet_vehicle_log_contract_act'}"
help="show the contract for this vehicle">
<field name="contract_count" widget="statinfo" string="Contracts"/>
</button>
<button name="act_show_log_cost"
type="object"
class="oe_stat_button"
icon="fa-usd"
help="show all the costs for this vehicle"
groups="fleet.group_fleet_manager">
<field name="cost_count" widget="statinfo" string="Costs"/>
</button>
<button name="return_action_to_open"
type="object"
class="oe_stat_button"
icon="fa-wrench"
context="{'xml_id':'fleet_vehicle_log_services_act'}"
help="show the services logs for this vehicle" >
<field name="service_count" widget="statinfo" string="Services"/>
</button>
<button name="return_action_to_open"
type="object"
class="oe_stat_button"
icon="fa-flask"
context="{'xml_id':'fleet_vehicle_log_fuel_act'}"
help="show the fuel logs for this vehicle" >
<field name="fuel_logs_count" widget="statinfo" string="Fuel"/>
</button>
<button name="return_action_to_open"
type="object"
class="oe_stat_button"
icon="fa-tachometer"
context="{'xml_id':'fleet_vehicle_odometer_act'}"
help="show the odometer logs for this vehicle" >
<field name="odometer_count" widget="statinfo" string="Odometer"/>
</button>
</div>
<group col="2" string="General Properties">
<group >

View File

@ -217,15 +217,7 @@ class gamification_challenge(osv.Model):
vals['user_ids'] = []
vals['user_ids'] += [(4, user_id) for user_id in user_ids]
create_res = super(gamification_challenge, self).create(cr, uid, vals, context=context)
# subscribe new users to the challenge
if vals.get('user_ids'):
# done with browse after super to be sure catch all after orm process
challenge = self.browse(cr, uid, create_res, context=context)
self.message_subscribe_users(cr, uid, [challenge.id], [user.id for user in challenge.user_ids], context=context)
return create_res
return super(gamification_challenge, self).create(cr, uid, vals, context=context)
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int,long)):
@ -240,6 +232,11 @@ class gamification_challenge(osv.Model):
write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context)
if vals.get('report_message_frequency', 'never') != 'never':
# _recompute_challenge_users do not set users for challenges with no reports, subscribing them now
for challenge in self.browse(cr, uid, ids, context=context):
self.message_subscribe(cr, uid, [challenge.id], [user.partner_id.id for user in challenge.user_ids], context=context)
if vals.get('state') == 'inprogress':
self._recompute_challenge_users(cr, uid, ids, context=context)
self._generate_goals_from_challenge(cr, uid, ids, context=context)
@ -264,6 +261,9 @@ class gamification_challenge(osv.Model):
- Create the missing goals (eg: modified the challenge to add lines)
- Update every running challenge
"""
if context is None:
context = {}
# start scheduled challenges
planned_challenge_ids = self.search(cr, uid, [
('state', '=', 'draft'),
@ -281,6 +281,9 @@ class gamification_challenge(osv.Model):
if not ids:
ids = self.search(cr, uid, [('state', '=', 'inprogress')], context=context)
# in cron mode, will do intermediate commits
# TODO in trunk: replace by parameter
context.update({'commit_gamification': True})
return self._update_all(cr, uid, ids, context=context)
def _update_all(self, cr, uid, ids, context=None):
@ -355,11 +358,6 @@ class gamification_challenge(osv.Model):
if write_op:
self.write(cr, uid, [challenge.id], {'user_ids': write_op}, context=context)
if to_remove_ids:
self.message_unsubscribe_users(cr, uid, [challenge.id], to_remove_ids, context=None)
if to_add_ids:
self.message_subscribe_users(cr, uid, [challenge.id], to_add_ids, context=context)
return True
@ -393,9 +391,9 @@ class gamification_challenge(osv.Model):
:param list(int) ids: the list of challenge concerned"""
goal_obj = self.pool.get('gamification.goal')
to_update = []
for challenge in self.browse(cr, uid, ids, context=context):
(start_date, end_date) = start_end_date_for_period(challenge.period)
to_update = []
# if no periodicity, use challenge dates
if not start_date and challenge.start_date:
@ -426,7 +424,15 @@ class gamification_challenge(osv.Model):
cr.execute(query, query_params)
user_with_goal_ids = cr.dictfetchall()
user_without_goal_ids = list(set([user.id for user in challenge.user_ids]) - set([user['user_id'] for user in user_with_goal_ids]))
participant_user_ids = [user.id for user in challenge.user_ids]
user_without_goal_ids = list(set(participant_user_ids) - set([user['user_id'] for user in user_with_goal_ids]))
user_squating_challenge_ids = list(set([user['user_id'] for user in user_with_goal_ids]) - set(participant_user_ids))
if user_squating_challenge_ids:
# users that used to match the challenge
goal_to_remove_ids = goal_obj.search(cr, uid, [('challenge_id', '=', challenge.id), ('user_id', 'in', user_squating_challenge_ids)], context=context)
goal_obj.unlink(cr, uid, goal_to_remove_ids, context=context)
values = {
'definition_id': line.definition_id.id,
@ -614,9 +620,10 @@ class gamification_challenge(osv.Model):
ctx.update({'challenge_lines': lines_boards})
body_html = temp_obj.render_template(cr, uid, challenge.report_template_id.body_html, 'gamification.challenge', challenge.id, context=ctx)
# send to every follower of the challenge
# send to every follower and participant of the challenge
self.message_post(cr, uid, challenge.id,
body=body_html,
partner_ids=[user.partner_id.id for user in challenge.user_ids],
context=context,
subtype='mail.mt_comment')
if challenge.report_message_group_id:
@ -698,34 +705,35 @@ class gamification_challenge(osv.Model):
rewarded_users = []
challenge_ended = end_date == yesterday.strftime(DF) or force
if challenge.reward_id and challenge_ended or challenge.reward_realtime:
for user in challenge.user_ids:
reached_goal_ids = self.pool.get('gamification.goal').search(cr, uid, [
('challenge_id', '=', challenge.id),
('user_id', '=', user.id),
('start_date', '=', start_date),
('end_date', '=', end_date),
('state', '=', 'reached')
], context=context)
if len(reached_goal_ids) == len(challenge.line_ids):
# not using start_date as intemportal goals have a start date but no end_date
reached_goals = self.pool.get('gamification.goal').read_group(cr, uid, [
('challenge_id', '=', challenge.id),
('end_date', '=', end_date),
('state', '=', 'reached')
], fields=['user_id'], groupby=['user_id'], context=context)
for reach_goals_user in reached_goals:
if reach_goals_user['user_id_count'] == len(challenge.line_ids):
# the user has succeeded every assigned goal
user_id = reach_goals_user['user_id'][0]
if challenge.reward_realtime:
badges = self.pool['gamification.badge.user'].search(cr, uid, [
('challenge_id', '=', challenge.id),
('badge_id', '=', challenge.reward_id.id),
('user_id', '=', user.id),
('user_id', '=', user_id),
], count=True, context=context)
if badges > 0:
# has already recieved the badge for this challenge
continue
self.reward_user(cr, uid, user.id, challenge.reward_id.id, challenge.id, context=context)
rewarded_users.append(user)
self.reward_user(cr, uid, user_id, challenge.reward_id.id, challenge.id, context=context)
rewarded_users.append(user_id)
if challenge_ended:
# open chatter message
message_body = _("The challenge %s is finished." % challenge.name)
if rewarded_users:
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([user.name for user in rewarded_users])))
user_names = self.pool['res.users'].name_get(cr, uid, rewarded_users, context=context)
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([name for (user_id, name) in user_names])))
else:
message_body += _("<br/>Nobody has succeeded to reach every goal, no badge is rewared for this challenge.")
@ -746,7 +754,10 @@ class gamification_challenge(osv.Model):
self.reward_user(cr, uid, third_user.id, challenge.reward_second_id.id, challenge.id, context=context)
message_body += "<br/> 3. %s - %s" % (third_user.name, challenge.reward_third_id.name)
self.message_post(cr, uid, challenge.id, body=message_body, context=context)
self.message_post(cr, uid, challenge.id,
partner_ids=[user.partner_id.id for user in challenge.user_ids],
body=message_body,
context=context)
return True

View File

@ -138,7 +138,6 @@ class gamification_goal(osv.Model):
_name = 'gamification.goal'
_description = 'Gamification goal instance'
_inherit = 'mail.thread'
def _get_completion(self, cr, uid, ids, field_name, arg, context=None):
"""Return the percentage of completeness of the goal, between 0 and 100"""
@ -226,8 +225,7 @@ class gamification_goal(osv.Model):
temp_obj = self.pool.get('email.template')
template_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'email_template_goal_reminder', context)
body_html = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.goal', goal.id, context=context)
self.message_post(cr, uid, goal.id, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment')
self.pool['mail.thread'].message_post(cr, uid, 0, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment')
return {'to_update': True}
return {}
@ -241,9 +239,9 @@ class gamification_goal(osv.Model):
the target value being reached, the goal is set as failed."""
if context is None:
context = {}
commit = context.get('commit_gamification', False)
goals_by_definition = {}
goals_to_write = {}
all_goals = {}
for goal in self.browse(cr, uid, ids, context=context):
if goal.state in ('draft', 'canceled'):
@ -251,10 +249,10 @@ class gamification_goal(osv.Model):
continue
goals_by_definition.setdefault(goal.definition_id, []).append(goal)
goals_to_write[goal.id] = {}
all_goals[goal.id] = goal
for definition, goals in goals_by_definition.items():
goals_to_write = dict((goal.id, {}) for goal in goals)
if definition.computation_mode == 'manually':
for goal in goals:
goals_to_write[goal.id].update(self._check_remind_delay(cr, uid, goal, context))
@ -345,22 +343,24 @@ class gamification_goal(osv.Model):
if new_value != goal.current:
goals_to_write[goal.id]['current'] = new_value
for goal_id, value in goals_to_write.items():
if not value:
continue
goal = all_goals[goal_id]
for goal_id, value in goals_to_write.items():
if not value:
continue
goal = all_goals[goal_id]
# check goal target reached
if (goal.definition_condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \
or (goal.definition_condition == 'lower' and value.get('current', goal.current) <= goal.target_goal):
value['state'] = 'reached'
# check goal target reached
if (goal.definition_condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \
or (goal.definition_condition == 'lower' and value.get('current', goal.current) <= goal.target_goal):
value['state'] = 'reached'
# check goal failure
elif goal.end_date and fields.date.today() > goal.end_date:
value['state'] = 'failed'
value['closed'] = True
if value:
self.write(cr, uid, [goal.id], value, context=context)
# check goal failure
elif goal.end_date and fields.date.today() > goal.end_date:
value['state'] = 'failed'
value['closed'] = True
if value:
self.write(cr, uid, [goal.id], value, context=context)
if commit:
cr.commit()
return True
def action_start(self, cr, uid, ids, context=None):

View File

@ -54,7 +54,13 @@
<!-- action buttons -->
<div class="oe_right oe_button_box">
<button type="action" name="%(goals_from_challenge_act)d" string="Related Goals" attrs="{'invisible': [('state','=','draft')]}" />
<button type="action"
name="%(goals_from_challenge_act)d"
class="oe_stat_button"
icon="fa-gift"
attrs="{'invisible': [('state','=','draft')]}">
<div>Related<br/>Goals</div>
</button>
</div>
<group>
<group>

View File

@ -86,10 +86,6 @@
</group>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>

View File

@ -628,13 +628,13 @@ class google_calendar(osv.AbstractModel):
else:
if event_to_synchronize[base_event][0][1].OE.event_id:
parent_oe_id = event_to_synchronize[base_event][0][1].OE.event_id
calendar_event.unlink(cr, uid, "%s-%s" % (parent_oe_id, new_google_event_id), unlink_level=1, context=context)
calendar_event.unlink(cr, uid, "%s-%s" % (parent_oe_id, new_google_event_id), can_be_deleted=True, context=context)
elif isinstance(actToDo, Delete):
if actSrc == 'GG':
self.delete_an_event(cr, uid, current_event[0], context=context)
elif actSrc == 'OE':
calendar_event.unlink(cr, uid, event.OE.event_id, unlink_level=0, context=context)
calendar_event.unlink(cr, uid, event.OE.event_id, can_be_deleted=False, context=context)
return True
def check_and_sync(self, cr, uid, oe_event, google_event, context):
@ -750,6 +750,9 @@ class calendar_event(osv.Model):
default['oe_update_date'] = False
return super(calendar_event, self).copy(cr, uid, id, default, context)
def unlink(self, cr, uid, ids, can_be_deleted=False, context=None):
return super(calendar_event, self).unlink(cr, uid, ids, can_be_deleted=can_be_deleted, context=context)
_columns = {
'oe_update_date': fields.datetime('OpenERP Update Date'),
}

View File

@ -248,7 +248,13 @@ class hr_employee(osv.osv):
'image': _get_default_image,
'color': 0,
}
def copy_data(self, cr, uid, ids, default=None, context=None):
if default is None:
default = {}
default.update({'child_ids': False})
return super(hr_employee, self).copy_data(cr, uid, ids, default, context=context)
def _broadcast_welcome(self, cr, uid, employee_id, context=None):
""" Broadcast the welcome message to all users in the employee company. """
employee = self.browse(cr, uid, employee_id, context=context)
@ -421,18 +427,23 @@ class hr_department(osv.osv):
res.append((record['id'], name))
return res
def copy(self, cr, uid, ids, default=None, context=None):
def copy_data(self, cr, uid, ids, default=None, context=None):
if default is None:
default = {}
default = default.copy()
default['member_ids'] = []
return super(hr_department, self).copy(cr, uid, ids, default, context=context)
return super(hr_department, self).copy_data(cr, uid, ids, default, context=context)
class res_users(osv.osv):
_name = 'res.users'
_inherit = 'res.users'
def copy_data(self, cr, uid, ids, default=None, context=None):
if default is None:
default = {}
default.update({'employee_ids': False})
return super(res_users, self).copy_data(cr, uid, ids, default, context=context)
_columns = {
'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'),
}

View File

@ -38,6 +38,13 @@ class hr_employee(osv.osv):
res[emp.id] = False
return res
def _contracts_count(self, cr, uid, ids, field_name, arg, context=None):
Contract = self.pool['hr.contract']
return {
employee_id: Contract.search_count(cr,uid, [('employee_id', '=', employee_id)], context=context)
for employee_id in ids
}
_columns = {
'manager': fields.boolean('Is a Manager'),
'medic_exam': fields.date('Medical Examination Date'),
@ -47,6 +54,7 @@ class hr_employee(osv.osv):
'vehicle_distance': fields.integer('Home-Work Dist.', help="In kilometers"),
'contract_ids': fields.one2many('hr.contract', 'employee_id', 'Contracts'),
'contract_id':fields.function(_get_latest_contract, string='Contract', type='many2one', relation="hr.contract", help='Latest contract of the employee'),
'contracts_count': fields.function(_contracts_count, type='integer', string='Contracts'),
}

View File

@ -21,7 +21,13 @@
<field name="arch" type="xml">
<data>
<xpath expr="//div[@name='button_box']" position="inside">
<button name="%(act_hr_employee_2_hr_contract)d" string="Contracts" type="action" groups="base.group_hr_manager"/>
<button name="%(act_hr_employee_2_hr_contract)d"
class="oe_stat_button"
icon="fa-book"
type="action"
groups="base.group_hr_manager">
<field name="contracts_count" widget="statinfo" string="Contracts"/>
</button>
</xpath>
<xpath expr="//field[@name='coach_id']" position="after">
<field name="manager"/>

View File

@ -101,11 +101,19 @@ Thanks,
class hr_employee(osv.Model):
_name = "hr.employee"
_inherit = "hr.employee"
_inherit="hr.employee"
def _appraisal_count(self, cr, uid, ids, field_name, arg, context=None):
Evaluation = self.pool['hr.evaluation.interview']
return {
employee_id: Evaluation.search_count(cr, uid, [('user_to_review_id', '=', employee_id)], context=context)
for employee_id in ids
}
_columns = {
'evaluation_plan_id': fields.many2one('hr_evaluation.plan', 'Appraisal Plan'),
'evaluation_date': fields.date('Next Appraisal Date', help="The date of the next appraisal is computed by the appraisal plan's dates (first appraisal + periodicity)."),
'appraisal_count': fields.function(_appraisal_count, type='integer', string='Appraisal Interviews'),
}
def run_employee_evaluation(self, cr, uid, automatic=False, use_new_cursor=False, context=None): # cronjob

View File

@ -147,7 +147,12 @@
</group>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button name="%(act_hr_employee_2_hr__evaluation_interview)d" string="Appraisal Interviews" type="action"/>
<button name="%(act_hr_employee_2_hr__evaluation_interview)d"
class="oe_stat_button"
icon="fa-sitemap"
type="action">
<field name="appraisal_count" widget="statinfo" string="Appraisals"/>
</button>
</xpath>
</field>
</record>

View File

@ -367,9 +367,9 @@ class hr_holidays(osv.osv):
'duration': record.number_of_days_temp * 8,
'description': record.notes,
'user_id': record.user_id.id,
'date': record.date_from,
'end_date': record.date_to,
'date_deadline': record.date_to,
'start': record.date_from,
'stop': record.date_to,
'allday': False,
'state': 'open', # to block that meeting date in the calendar
'class': 'confidential'
}
@ -549,6 +549,13 @@ class hr_employee(osv.osv):
result[holiday.employee_id.id]['current_leave_id'] = holiday.holiday_status_id.id
return result
def _leaves_count(self, cr, uid, ids, field_name, arg, context=None):
Holidays = self.pool['hr.holidays']
return {
employee_id: Holidays.search_count(cr,uid, [('employee_id', '=', employee_id)], context=context)
for employee_id in ids
}
_columns = {
'remaining_leaves': fields.function(_get_remaining_days, string='Remaining Legal Leaves', fnct_inv=_set_remaining_days, type="float", help='Total number of legal leaves allocated to this employee, change this value to create allocation/leave request. Total based on all the leave types without overriding limit.'),
'current_leave_state': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Status", type="selection",
@ -557,6 +564,8 @@ class hr_employee(osv.osv):
'current_leave_id': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Type",type='many2one', relation='hr.holidays.status'),
'leave_date_from': fields.function(_get_leave_status, multi='leave_status', type='date', string='From Date'),
'leave_date_to': fields.function(_get_leave_status, multi='leave_status', type='date', string='To Date'),
'leaves_count': fields.function(_leaves_count, type='integer', string='Leaves'),
}

View File

@ -483,7 +483,13 @@
</group>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button name="%(act_hr_employee_holiday_request)d" string="Leaves" type="action" groups="base.group_hr_user"/>
<button name="%(act_hr_employee_holiday_request)d"
type="action"
class="oe_stat_button"
icon="fa-calendar"
groups="base.group_hr_user">
<field name="leaves_count" widget="statinfo" string="Leaves"/>
</button>
</xpath>
</field>
</record>

View File

@ -258,6 +258,12 @@ class hr_payslip(osv.osv):
for r in res:
result[r[0]].append(r[1])
return result
def _count_detail_payslip(self, cr, uid, ids, field_name, arg, context=None):
res = {}
for details in self.browse(cr, uid, ids, context=context):
res[details.id] = len(details.line_ids)
return res
_columns = {
'struct_id': fields.many2one('hr.payroll.structure', 'Structure', readonly=True, states={'draft': [('readonly', False)]}, help='Defines the rules that have to be applied to this payslip, accordingly to the contract chosen. If you let empty the field contract, this field isn\'t mandatory anymore and thus the rules applied will be all the rules set on the structure of all contracts of the employee valid for the chosen period'),
@ -276,7 +282,6 @@ class hr_payslip(osv.osv):
\n* If the payslip is under verification, the status is \'Waiting\'. \
\n* If the payslip is confirmed then status is set to \'Done\'.\
\n* When user cancel payslip the status is \'Rejected\'.'),
# 'line_ids': fields.one2many('hr.payslip.line', 'slip_id', 'Payslip Line', required=False, readonly=True, states={'draft': [('readonly', False)]}),
'line_ids': one2many_mod2('hr.payslip.line', 'slip_id', 'Payslip Lines', readonly=True, states={'draft':[('readonly',False)]}),
'company_id': fields.many2one('res.company', 'Company', required=False, readonly=True, states={'draft': [('readonly', False)]}),
'worked_days_line_ids': fields.one2many('hr.payslip.worked_days', 'payslip_id', 'Payslip Worked Days', required=False, readonly=True, states={'draft': [('readonly', False)]}),
@ -287,6 +292,7 @@ class hr_payslip(osv.osv):
'details_by_salary_rule_category': fields.function(_get_lines_salary_rule_category, method=True, type='one2many', relation='hr.payslip.line', string='Details by Salary Rule Category'),
'credit_note': fields.boolean('Credit Note', help="Indicates this payslip has a refund of another", readonly=True, states={'draft': [('readonly', False)]}),
'payslip_run_id': fields.many2one('hr.payslip.run', 'Payslip Batches', readonly=True, states={'draft': [('readonly', False)]}),
'payslip_count': fields.function(_count_detail_payslip, type='integer', string="Payslip Computation Details"),
}
_defaults = {
'date_from': lambda *a: time.strftime('%Y-%m-01'),
@ -972,9 +978,17 @@ class hr_employee(osv.osv):
res[employee.id] = {'basic': result['sum']}
return res
def _payslip_count(self, cr, uid, ids, field_name, arg, context=None):
Payslip = self.pool['hr.payslip']
return {
employee_id: Payslip.search_count(cr,uid, [('employee_id', '=', employee_id)], context=context)
for employee_id in ids
}
_columns = {
'slip_ids':fields.one2many('hr.payslip', 'employee_id', 'Payslips', required=False, readonly=True),
'total_wage': fields.function(_calculate_total_wage, method=True, type='float', string='Total Basic Salary', digits_compute=dp.get_precision('Payroll'), help="Sum of all current contract's wage of employee."),
'payslip_count': fields.function(_payslip_count, type='integer', string='Payslips'),
}

View File

@ -222,7 +222,12 @@
</header>
<sheet>
<div class="oe_right oe_button_box">
<button name="%(act_payslip_lines)d" string="Payslip Computation Details" type="action"/>
<button name="%(act_payslip_lines)d"
class="oe_stat_button"
icon="fa-money"
type="action">
<field name="payslip_count" widget="statinfo" string="Payslip" help="Payslip Computation Details"/>
</button>
</div>
<div class="oe_title">
<label for="employee_id" class="oe_edit_only"/>
@ -387,7 +392,13 @@
<field name="arch" type="xml">
<data>
<xpath expr="//div[@name='button_box']" position="inside">
<button name="%(act_hr_employee_payslip_list)d" string="Payslips" type="action" groups="base.group_hr_user"/>
<button name="%(act_hr_employee_payslip_list)d"
class="oe_stat_button"
icon="fa-money"
type="action"
groups="base.group_hr_user">
<field name="payslip_count" widget="statinfo" string="Payslips"/>
</button>
</xpath>
</data>
</field>

View File

@ -529,6 +529,16 @@ class hr_job(osv.osv):
], context=context)
return res
def _count_all(self, cr, uid, ids, field_name, arg, context=None):
Applicant = self.pool['hr.applicant']
return {
job_id: {
'application_count': Applicant.search_count(cr,uid, [('job_id', '=', job_id)], context=context),
'documents_count': len(self._get_attached_docs(cr, uid, [job_id], field_name, arg, context=context)[job_id])
}
for job_id in ids
}
_columns = {
'survey_id': fields.many2one('survey.survey', 'Interview Form', help="Choose an interview form for this job position and you will be able to print/answer this interview from all applicants who apply for this job"),
'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True,
@ -536,8 +546,10 @@ class hr_job(osv.osv):
"create new applicants for this job position."),
'address_id': fields.many2one('res.partner', 'Job Location', help="Address where employees are working"),
'application_ids': fields.one2many('hr.applicant', 'job_id', 'Applications'),
'application_count': fields.function(_count_all, type='integer', string='Applications', multi=True),
'manager_id': fields.related('department_id', 'manager_id', type='many2one', string='Department Manager', relation='hr.employee', readonly=True, store=True),
'document_ids': fields.function(_get_attached_docs, type='one2many', relation='ir.attachment', string='Applications'),
'documents_count': fields.function(_count_all, type='integer', string='Documents', multi=True),
'user_id': fields.many2one('res.users', 'Recruitment Responsible', track_visibility='onchange'),
'color': fields.integer('Color Index'),
}

View File

@ -74,16 +74,33 @@
</header>
<sheet>
<div class="oe_right oe_button_box">
<button name="action_makeMeeting" type="object"
string="Schedule Meeting" help="Schedule interview with this applicant"/>
<button name="action_start_survey" type="object"
string="Start Interview" help="Answer related job question"
<button name="action_makeMeeting" class="oe_stat_button" icon="fa-calendar" type="object"
help="Schedule interview with this applicant">
<div>Schedule<br/>Meeting</div>
</button>
<button name="action_start_survey"
class="oe_stat_button"
icon="fa-user"
type="object"
help="Answer related job question"
context="{'survey_id': survey}"
attrs="{'invisible':[('survey','=',False)]}"/>
<button name="action_print_survey" type="object"
string="Print Interview" help="Print interview report"
attrs="{'invisible':[('survey','=',False)]}"/>
<button name="action_get_attachment_tree_view" string="Documents" type="object"/>
attrs="{'invisible':[('survey','=',False)]}">
<div>Start<br/>Interview</div>
</button>
<button name="action_print_survey"
class="oe_stat_button"
icon="fa-print"
type="object"
help="Print interview report"
attrs="{'invisible':[('survey','=',False)]}">
<div>Print<br/>Interview</div>
</button>
<button name="action_get_attachment_tree_view"
class="oe_stat_button"
icon="fa-book"
type="object">
<field name="attachment_number" widget="statinfo" string="Documents"/>
</button>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
@ -367,10 +384,23 @@
<xpath expr="//field[@name='department_id']" position="after">
<field name="user_id" class="oe_inline"/>
</xpath>
<div name="buttons" position="inside">
<button string="Applications" name="%(action_hr_job_applications)d" context="{'default_user_id': user_id}" type="action"/>
<button string="Documents" name="action_get_attachment_tree_view" type="object"/>
</div>
<xpath expr="//div[@name='buttons']" position="inside">
<div class="oe_button_box" >
<button class="oe_stat_button"
icon="fa-file-o"
name="%(action_hr_job_applications)d"
context="{'default_user_id': user_id}"
type="action">
<field name="application_count" widget="statinfo" string="Applications"/>
</button>
<button class="oe_stat_button"
icon="fa-book"
name="action_get_attachment_tree_view"
type="object">
<field name="documents_count" widget="statinfo" string="Documents"/>
</button>
</div>
</xpath>
</field>
</record>

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
"PO-Revision-Date: 2012-12-13 14:50+0000\n"
"Last-Translator: Rui Franco (multibase.pt) <Unknown>\n"
"PO-Revision-Date: 2014-05-06 17:00+0000\n"
"Last-Translator: Daniel Reis <Unknown>\n"
"Language-Team: Portuguese <pt@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:32+0000\n"
"X-Generator: Launchpad (build 16985)\n"
"X-Launchpad-Export-Date: 2014-05-07 06:27+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: hr_recruitment
#: help:hr.applicant,active:0
@ -53,6 +53,8 @@ msgid ""
"This stage is not visible, for example in status bar or kanban view, when "
"there are no records in that stage to display."
msgstr ""
"Esta Etapa não é visível, por exemplo na barra de estado ou na vista kanban, "
"quando não existirem registos a exibir nessa Etapa."
#. module: hr_recruitment
#: model:hr.recruitment.degree,name:hr_recruitment.degree_graduate
@ -174,7 +176,7 @@ msgstr "empregado"
#. module: hr_recruitment
#: field:hr.config.settings,fetchmail_applicants:0
msgid "Create applicants from an incoming email account"
msgstr ""
msgstr "Criar candidaturas a partir de uma conta de email"
#. module: hr_recruitment
#: view:hr.recruitment.report:0
@ -237,11 +239,13 @@ msgid ""
"Stages of the recruitment process may be different per department. If this "
"stage is common to all departments, keep this field empty."
msgstr ""
"As etapas do processo de recrutamento podem ser diferentes por departamento. "
"Se esta etapa for comum a todos, deixe esta campo vazio."
#. module: hr_recruitment
#: help:hr.applicant,message_unread:0
msgid "If checked new messages require your attention."
msgstr ""
msgstr "Se marcado, as novas mensagens requerem a sua atenção."
#. module: hr_recruitment
#: field:hr.applicant,color:0
@ -375,7 +379,7 @@ msgstr "Monstro"
#. module: hr_recruitment
#: model:mail.message.subtype,name:hr_recruitment.mt_applicant_hired
msgid "Applicant Hired"
msgstr ""
msgstr "Candidato Contratado"
#. module: hr_recruitment
#: field:hr.applicant,email_from:0
@ -471,7 +475,7 @@ msgstr "Segunda entrevista"
#. module: hr_recruitment
#: model:ir.actions.act_window,name:hr_recruitment.hr_job_stage_act
msgid "Recruitment / Applicants Stages"
msgstr "Recrutamento/Candidatos a Estágios"
msgstr "Etapas de Recrutamento / Candidatura"
#. module: hr_recruitment
#: field:hr.applicant,salary_expected:0
@ -513,7 +517,7 @@ msgstr "Candidato"
#. module: hr_recruitment
#: help:hr.recruitment.stage,sequence:0
msgid "Gives the sequence order when displaying a list of stages."
msgstr "Dá a sequência ordenada ao exibir a lista de estágios."
msgstr "Define a ordem de apresentação das etapas."
#. module: hr_recruitment
#: field:hr.applicant,partner_id:0
@ -570,7 +574,7 @@ msgstr "Contrar & criar empregado"
#. module: hr_recruitment
#: model:mail.message.subtype,description:hr_recruitment.mt_applicant_hired
msgid "Applicant hired"
msgstr ""
msgstr "Candidato contratado"
#. module: hr_recruitment
#: view:hr.applicant:0
@ -601,12 +605,12 @@ msgstr "Dezembro"
#: code:addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py:39
#, python-format
msgid "A contact is already defined on this job request."
msgstr ""
msgstr "Já foi definido um contacto para este pedido de recrutamento."
#. module: hr_recruitment
#: field:hr.applicant,categ_ids:0
msgid "Tags"
msgstr ""
msgstr "Marcadores"
#. module: hr_recruitment
#: model:ir.model,name:hr_recruitment.model_hr_applicant_category
@ -622,7 +626,7 @@ msgstr "Mês"
#. module: hr_recruitment
#: view:hr.applicant:0
msgid "Answer related job question"
msgstr ""
msgstr "Resposta de questão de candidatura"
#. module: hr_recruitment
#: model:hr.recruitment.stage,name:hr_recruitment.stage_job2
@ -654,7 +658,7 @@ msgstr "ou"
#. module: hr_recruitment
#: model:mail.message.subtype,name:hr_recruitment.mt_applicant_refused
msgid "Applicant Refused"
msgstr ""
msgstr "Candidato eliminado"
#. module: hr_recruitment
#: view:hr.applicant:0
@ -727,7 +731,7 @@ msgstr "Fechado"
#. module: hr_recruitment
#: view:hr.recruitment.stage:0
msgid "Stage Definition"
msgstr "Definição estágio"
msgstr "Definição da etapa"
#. module: hr_recruitment
#: field:hr.recruitment.report,delay_close:0
@ -764,7 +768,7 @@ msgstr "Marcar entrevista com este candidato"
#: code:addons/hr_recruitment/hr_recruitment.py:397
#, python-format
msgid "Applicant <b>created</b>"
msgstr ""
msgstr "Candidato <b>criado</b>"
#. module: hr_recruitment
#: view:hr.applicant:0
@ -844,7 +848,7 @@ msgstr "Outubro"
#. module: hr_recruitment
#: field:hr.config.settings,module_document_ftp:0
msgid "Allow the automatic indexation of resumes"
msgstr ""
msgstr "Fazer a indexação automática de currículos"
#. module: hr_recruitment
#: field:hr.applicant,salary_proposed_extra:0
@ -896,7 +900,7 @@ msgstr "Gostaria de criar um funcionário?"
#. module: hr_recruitment
#: view:hr.applicant:0
msgid "Degree:"
msgstr ""
msgstr "Grau Académico:"
#. module: hr_recruitment
#: view:hr.recruitment.report:0
@ -910,9 +914,9 @@ msgid ""
"forget to specify the department if your recruitment process is different "
"according to the job position."
msgstr ""
"Verifique se as seguintes etapas estão combinando com o processo de "
"recrutamento. Não se esqueça de especificar o serviço se o seu processo de "
"recrutamento é diferente de acordo com o cargo."
"Verifique se as seguintes etapas correspondem ao seu processo de "
"recrutamento. Não se esqueça de indicar o Departamento, caso o seu processo "
"de recrutamento seja diferente de acordo com o cargo ."
#. module: hr_recruitment
#: view:hr.config.settings:0
@ -979,7 +983,7 @@ msgstr "Dá a sequência é ordenada ao exibir uma lista de graus."
#. module: hr_recruitment
#: model:mail.message.subtype,description:hr_recruitment.mt_stage_changed
msgid "Stage changed"
msgstr ""
msgstr "Etapa mudou"
#. module: hr_recruitment
#: view:hr.applicant:0
@ -1053,7 +1057,7 @@ msgstr "Descrição"
#. module: hr_recruitment
#: model:mail.message.subtype,name:hr_recruitment.mt_stage_changed
msgid "Stage Changed"
msgstr ""
msgstr "Etapa mudou"
#. module: hr_recruitment
#: selection:hr.recruitment.report,month:0
@ -1073,7 +1077,7 @@ msgstr "Passa Palavra"
#. module: hr_recruitment
#: field:hr.recruitment.stage,fold:0
msgid "Hide in views if empty"
msgstr ""
msgstr "Ocultar nas vistas se vazio"
#. module: hr_recruitment
#: help:hr.config.settings,module_document_ftp:0
@ -1202,7 +1206,7 @@ msgstr "Histórico de mensagens e comunicação"
#. module: hr_recruitment
#: model:mail.message.subtype,description:hr_recruitment.mt_applicant_refused
msgid "Applicant refused"
msgstr ""
msgstr "Candidato eliminado"
#. module: hr_recruitment
#: field:hr.recruitment.stage,department_id:0
@ -1243,6 +1247,10 @@ msgid ""
"related to the status 'Close', when your document reach this stage, it will "
"be automatically closed."
msgstr ""
"O estado correspondente à etapa. O estado do seu documento irá mudar "
"automaticamente de acordo com a sua etapa. Por exemplo, se a uma etapa "
"corresponder o estado 'fechado', quando um documento passar para essa etapa "
"ele será automaticamente fechado."
#. module: hr_recruitment
#: help:hr.applicant,salary_proposed_extra:0
@ -1262,7 +1270,7 @@ msgstr "Abrir"
#. module: hr_recruitment
#: view:board.board:0
msgid "Applications to be Processed"
msgstr ""
msgstr "Candidaturas a tratar"
#. module: hr_recruitment
#: view:hr.applicant:0

View File

@ -118,10 +118,8 @@
<label for="use_timesheets"/>
</xpath>
<xpath expr='//div[@name="buttons"]' position='inside'>
<button string="Costs and Revenues"
name="%(act_analytic_cost_revenue)d"
type="action"/>
<button class="oe_inline oe_stat_button" type="action" name="%(act_analytic_cost_revenue)d"
icon="fa-usd" string="Cost/Revenue" widget="statinfo"/>
</xpath>
</field>
</record>

View File

@ -22,9 +22,11 @@
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
from pytz import timezone
import pytz
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.translate import _
class hr_timesheet_sheet(osv.osv):
@ -132,6 +134,17 @@ class hr_timesheet_sheet(osv.osv):
for sheet in self.browse(cr, uid, ids, context=context):
if sheet.employee_id.id not in employee_ids: employee_ids.append(sheet.employee_id.id)
return hr_employee.attendance_action_change(cr, uid, employee_ids, context=context)
def _count_all(self, cr, uid, ids, field_name, arg, context=None):
Timesheet = self.pool['hr.analytic.timesheet']
Attendance = self.pool['hr.attendance']
return {
sheet_id: {
'timesheet_activity_count': Timesheet.search_count(cr,uid, [('sheet_id','=', sheet_id)], context=context),
'attendance_count': Attendance.search_count(cr,uid, [('sheed_id', '=', sheet_id)], context=context)
}
for sheet_id in ids
}
_columns = {
'name': fields.char('Note', size=64, select=1,
@ -163,6 +176,8 @@ class hr_timesheet_sheet(osv.osv):
'account_ids': fields.one2many('hr_timesheet_sheet.sheet.account', 'sheet_id', 'Analytic accounts', readonly=True),
'company_id': fields.many2one('res.company', 'Company'),
'department_id':fields.many2one('hr.department','Department'),
'timesheet_activity_count': fields.function(_count_all, type='integer', string='Timesheet Activities', multi=True),
'attendance_count': fields.function(_count_all, type='integer', string="Attendances", multi=True),
}
def _default_date_from(self, cr, uid, context=None):
@ -397,22 +412,56 @@ class hr_attendance(osv.osv):
attendance_ids.extend([row[0] for row in cr.fetchall()])
return attendance_ids
def _get_current_sheet(self, cr, uid, employee_id, date=False, context=None):
def _get_attendance_employee_tz(self, cr, uid, employee_id, date, context=None):
""" Simulate timesheet in employee timezone
Return the attendance date in string format in the employee
tz converted from utc timezone as we consider date of employee
timesheet is in employee timezone
"""
employee_obj = self.pool['hr.employee']
tz = False
if employee_id:
employee = employee_obj.browse(cr, uid, employee_id, context=context)
tz = employee.user_id.partner_id.tz
if not date:
date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
# ending date with no time to avoid timesheet with early date_to
date_to = date[0:10]+' 00:00:00'
# limit=1 because only one sheet possible for an employee between 2 dates
sheet_ids = self.pool.get('hr_timesheet_sheet.sheet').search(cr, uid, [
('date_to', '>=', date_to), ('date_from', '<=', date),
('employee_id', '=', employee_id)
], limit=1, context=context)
att_tz = timezone(tz or 'utc')
attendance_dt = datetime.strptime(date, DEFAULT_SERVER_DATETIME_FORMAT)
att_tz_dt = pytz.utc.localize(attendance_dt)
att_tz_dt = att_tz_dt.astimezone(att_tz)
# We take only the date omiting the hours as we compare with timesheet
# date_from which is a date format thus using hours would lead to
# be out of scope of timesheet
att_tz_date_str = datetime.strftime(att_tz_dt, DEFAULT_SERVER_DATE_FORMAT)
return att_tz_date_str
def _get_current_sheet(self, cr, uid, employee_id, date=False, context=None):
sheet_obj = self.pool['hr_timesheet_sheet.sheet']
if not date:
date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
att_tz_date_str = self._get_attendance_employee_tz(
cr, uid, employee_id,
date=date, context=context)
sheet_ids = sheet_obj.search(cr, uid,
[('date_from', '<=', att_tz_date_str),
('date_to', '>=', att_tz_date_str),
('employee_id', '=', employee_id)],
limit=1, context=context)
return sheet_ids and sheet_ids[0] or False
def _sheet(self, cursor, user, ids, name, args, context=None):
res = {}.fromkeys(ids, False)
for attendance in self.browse(cursor, user, ids, context=context):
res[attendance.id] = self._get_current_sheet(cursor, user, attendance.employee_id.id, attendance.name, context=context)
res[attendance.id] = self._get_current_sheet(
cursor, user, attendance.employee_id.id, attendance.name,
context=context)
return res
_columns = {
@ -434,10 +483,13 @@ class hr_attendance(osv.osv):
sheet_id = context.get('sheet_id') or self._get_current_sheet(cr, uid, vals.get('employee_id'), vals.get('name'), context=context)
if sheet_id:
att_tz_date_str = self._get_attendance_employee_tz(
cr, uid, vals.get('employee_id'),
date=vals.get('name'), context=context)
ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, sheet_id, context=context)
if ts.state not in ('draft', 'new'):
raise osv.except_osv(_('Error!'), _('You can not enter an attendance in a submitted timesheet. Ask your manager to reset it before adding attendance.'))
elif ts.date_from > vals.get('name') or ts.date_to < vals.get('name'):
elif ts.date_from > att_tz_date_str or ts.date_to < att_tz_date_str:
raise osv.except_osv(_('User Error!'), _('You can not enter an attendance date outside the current timesheet dates.'))
return super(hr_attendance,self).create(cr, uid, vals, context=context)
@ -587,6 +639,22 @@ class res_company(osv.osv):
'timesheet_max_difference': lambda *args: 0.0
}
class hr_employee(osv.osv):
'''
Employee
'''
_inherit = 'hr.employee'
_description = 'Employee'
def _timesheet_count(self, cr, uid, ids, field_name, arg, context=None):
Sheet = self.pool['hr_timesheet_sheet.sheet']
return {
employee_id: Sheet.search_count(cr,uid, [('employee_id', '=', employee_id)], context=context)
for employee_id in ids
}
_columns = {
'timesheet_count': fields.function(_timesheet_count, type='integer', string='Timesheets'),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -73,10 +73,15 @@
<div class="oe_right oe_button_box" name="buttons">
<button type="action"
name="%(act_hr_timesheet_sheet_sheet_2_hr_analytic_timesheet)d"
string="Timesheet Activities" />
class="oe_stat_button"
icon="fa-clock-o">
<field name="timesheet_activity_count" widget="statinfo" string="Timesheet" help="Timesheet Activities"/>
</button>
<button type="action" groups="base.group_hr_attendance"
name="%(act_hr_timesheet_sheet_sheet_2_hr_attendance)d"
string="Attendances" />
class="oe_stat_button" icon="fa-users"
name="%(act_hr_timesheet_sheet_sheet_2_hr_attendance)d">
<field name="attendance_count" widget="statinfo" string="Attendances"/>
</button>
</div>
<div class="oe_title">
<label for="employee_id" class="oe_edit_only"/>
@ -365,7 +370,13 @@
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_employee_extd_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="%(act_hr_employee_2_hr_timesheet)d" string="Timesheets" type="action" groups="base.group_hr_manager"/>
<button name="%(act_hr_employee_2_hr_timesheet)d"
type="action"
class="oe_stat_button"
icon="fa-clock-o"
groups="base.group_hr_manager">
<field name="timesheet_count" widget="statinfo" string="Timesheets"/>
</button>
</xpath>
</field>
</record>

View File

@ -6,6 +6,7 @@ from openerp import SUPERUSER_ID
from openerp import http
from openerp.http import request
from openerp.addons.web.controllers.main import content_disposition
import mimetypes
class MailController(http.Controller):
@ -19,10 +20,11 @@ class MailController(http.Controller):
if res:
filecontent = base64.b64decode(res.get('base64'))
filename = res.get('filename')
content_type = mimetypes.guess_type(filename)
if filecontent and filename:
return request.make_response(
filecontent,
headers=[('Content-Type', 'application/octet-stream'),
headers=[('Content-Type', content_type[0] or 'application/octet-stream'),
('Content-Disposition', content_disposition(filename))])
return request.not_found()

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
"PO-Revision-Date: 2013-11-19 10:40+0000\n"
"Last-Translator: Guipo Hao <hrlpool@sohu.com>\n"
"PO-Revision-Date: 2014-05-06 01:37+0000\n"
"Last-Translator: Victor Yu <vivianyw@163.com>\n"
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:43+0000\n"
"X-Generator: Launchpad (build 16985)\n"
"X-Launchpad-Export-Date: 2014-05-07 06:27+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: mail
#: view:mail.followers:0
@ -1607,7 +1607,7 @@ msgstr "相关单据模型"
#: code:addons/mail/static/src/xml/mail.xml:338
#, python-format
msgid "unlike"
msgstr "-1"
msgstr "unlike"
#. module: mail
#: help:mail.compose.message,author_id:0

View File

@ -238,6 +238,7 @@ class mail_mail(osv.Model):
if context is None:
context = {}
ir_mail_server = self.pool.get('ir.mail_server')
ir_attachment = self.pool['ir.attachment']
for mail in self.browse(cr, SUPERUSER_ID, ids, context=context):
try:
# TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
@ -248,10 +249,15 @@ class mail_mail(osv.Model):
model = None
if model:
context['model_name'] = model.name
# handle attachments
attachments = []
for attach in mail.attachment_ids:
attachments.append((attach.datas_fname, base64.b64decode(attach.datas)))
# load attachment binary data with a separate read(), as prefetching all
# `datas` (binary field) could bloat the browse cache, triggerring
# soft/hard mem limits with temporary data.
attachment_ids = [a.id for a in mail.attachment_ids]
attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
for a in ir_attachment.read(cr, SUPERUSER_ID, attachment_ids,
['datas_fname', 'datas'])]
# specific behavior to customize the send email for notified partners
email_list = []
if mail.email_to:
@ -300,6 +306,14 @@ class mail_mail(osv.Model):
# /!\ can't use mail.state here, as mail.refresh() will cause an error
# see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1
self._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=mail_sent)
_logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id)
except MemoryError:
# prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
# instead of marking the mail as failed
_logger.exception('MemoryError while processing mail with ID %r and Msg-Id %r. '\
'Consider raising the --limit-memory-hard startup option',
mail.id, mail.message_id)
raise
except Exception as e:
_logger.exception('failed sending mail.mail %s', mail.id)
mail.write({'state': 'exception'})

View File

@ -385,7 +385,7 @@ class mail_thread(osv.AbstractModel):
if not context.get('mail_notrack'):
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=track_ctx)
if tracked_fields:
initial_values = {thread_id: dict((item, False) for item in tracked_fields)}
initial_values = {thread_id: dict.fromkeys(tracked_fields, False)}
self.message_track(cr, uid, [thread_id], tracked_fields, initial_values, context=track_ctx)
return thread_id
@ -398,25 +398,24 @@ class mail_thread(osv.AbstractModel):
track_ctx = dict(context)
if 'lang' not in track_ctx:
track_ctx['lang'] = self.pool.get('res.users').browse(cr, uid, uid, context=context).lang
tracked_fields = None
if not context.get('mail_notrack'):
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=track_ctx)
else:
tracked_fields = []
if tracked_fields:
records = self.browse(cr, uid, ids, context=track_ctx)
initial_values = dict((this.id, dict((key, getattr(this, key)) for key in tracked_fields.keys())) for this in records)
initial_values = dict((record.id, dict((key, getattr(record, key)) for key in tracked_fields))
for record in records)
# Perform write, update followers
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context, values=values)
if not context.get('mail_notrack'):
# Perform the tracking
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
else:
tracked_fields = None
# Perform the tracking
if tracked_fields:
self.message_track(cr, uid, ids, tracked_fields, initial_values, context=track_ctx)
return result
def unlink(self, cr, uid, ids, context=None):
@ -434,14 +433,14 @@ class mail_thread(osv.AbstractModel):
fol_obj.unlink(cr, SUPERUSER_ID, fol_ids, context=context)
return res
def copy(self, cr, uid, id, default=None, context=None):
def copy_data(self, cr, uid, id, default=None, context=None):
# avoid tracking multiple temporary changes during copy
context = dict(context or {}, mail_notrack=True)
default = default or {}
default['message_ids'] = []
default['message_follower_ids'] = []
return super(mail_thread, self).copy(cr, uid, id, default=default, context=context)
return super(mail_thread, self).copy_data(cr, uid, id, default=default, context=context)
#------------------------------------------------------
# Automatically log tracked fields
@ -453,14 +452,15 @@ class mail_thread(osv.AbstractModel):
:return list: a list of (field_name, column_info obj), containing
always tracked fields and modified on_change fields
"""
lst = []
tracked_fields = []
for name, column_info in self._all_columns.items():
visibility = getattr(column_info.column, 'track_visibility', False)
if visibility == 'always' or (visibility == 'onchange' and name in updated_fields) or name in self._track:
lst.append(name)
if not lst:
return lst
return self.fields_get(cr, uid, lst, context=context)
tracked_fields.append(name)
if tracked_fields:
return self.fields_get(cr, uid, tracked_fields, context=context)
return {}
def message_track(self, cr, uid, ids, tracked_fields, initial_values, context=None):
@ -1871,4 +1871,4 @@ class mail_thread(osv.AbstractModel):
message_obj.write(cr, uid, msg_ids_comment, {"res_id" : new_res_id, "model" : new_model}, context=context)
message_obj.write(cr, uid, msg_ids_not_comment, {"res_id" : new_res_id, "model" : new_model, "subtype_id" : None}, context=context)
return True
return True

View File

@ -34,14 +34,14 @@ class project_configuration(osv.TransientModel):
}
def get_default_alias_domain(self, cr, uid, ids, context=None):
alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context)
if not alias_domain:
alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", default=None, context=context)
if alias_domain is None:
domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "web.base.url", context=context)
try:
alias_domain = urlparse.urlsplit(domain).netloc.split(':')[0]
except Exception:
pass
return {'alias_domain': alias_domain}
return {'alias_domain': alias_domain or False}
def set_alias_domain(self, cr, uid, ids, context=None):
config_parameters = self.pool.get("ir.config_parameter")

View File

@ -187,7 +187,7 @@
</span>
</t>
</td>
<td><div class="oe_view_manager_view_search" t-opentag="true"/></td>
<td><div class="oe_view_manager_view_search"/></td>
</tr>
</tbody>
</table>

View File

@ -85,6 +85,15 @@ translate_selections = {
class marketing_campaign(osv.osv):
_name = "marketing.campaign"
_description = "Marketing Campaign"
def _count_segments(self, cr, uid, ids, field_name, arg, context=None):
res = {}
try:
for segments in self.browse(cr, uid, ids, context=context):
res[segments.id] = len(segments.segment_ids)
except:
pass
return res
_columns = {
'name': fields.char('Name', size=64, required=True),
@ -121,6 +130,8 @@ Normal - the campaign runs normally and automatically sends all emails and repor
'activity_ids': fields.one2many('marketing.campaign.activity',
'campaign_id', 'Activities'),
'fixed_cost': fields.float('Fixed Cost', help="Fixed cost for running this campaign. You may also specify variable cost and revenue on each campaign activity. Cost and Revenue statistics are included in Campaign Reporting.", digits_compute=dp.get_precision('Product Price')),
'segment_ids': fields.one2many('marketing.campaign.segment', 'campaign_id', 'Segments', readonly=False),
'segments_count': fields.function(_count_segments, type='integer', string='Segments')
}
_defaults = {

View File

@ -64,12 +64,21 @@
<div class="oe_right oe_button_box" name="button_box">
<button type="action"
name="%(act_marketing_campaing_stat)d"
string="Campaign Statistics"/>
class="oe_stat_button"
icon="fa-keyboard-o"
help="Marketing Campaign Statistics">
<div>Campaign<br/>Statistics</div>
</button>
<button type="action"
name="%(act_marketing_campaing_segment_opened)d"
string="Segments"/>
class="oe_stat_button"
icon="fa-table">
<field name="segments_count" widget="statinfo" string="Segments"/>
</button>
<button type="action"
name="%(act_marketing_campaing_followup)d"
class="oe_stat_button"
icon="fa-thumbs-up"
string="Follow-Up"/>
</div>
<div class="oe_title">

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