[MERGE] merge from trunk addons

bzr revid: mra@mra-laptop-20101222112024-yt47p2bd7kwvcqwy
bzr revid: mra@mra-laptop-20101223041057-w5s862hm3fajcqkr
This commit is contained in:
Mustufa Rangwala 2010-12-23 09:40:57 +05:30
commit 6002cc0ec2
90 changed files with 1625 additions and 1182 deletions

View File

@ -33,5 +33,6 @@ import report
import product
import sequence
import company
import res_currency
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -708,6 +708,7 @@ class account_journal(osv.osv):
return self.name_get(cr, user, ids, context=context)
def onchange_type(self, cr, uid, ids, type, currency, context=None):
obj_data = self.pool.get('ir.model.data')
user_pool = self.pool.get('res.users')
@ -730,7 +731,6 @@ class account_journal(osv.osv):
user = user_pool.browse(cr, uid, uid)
if type in ('cash', 'bank') and currency and user.company_id.currency_id.id != currency:
view_id = 'account_journal_bank_view_multi'
data_id = obj_data.search(cr, uid, [('model','=','account.journal.view'), ('name','=',view_id)])
data = obj_data.browse(cr, uid, data_id[0], context=context)

View File

@ -225,9 +225,11 @@ class account_bank_statement(osv.osv):
account_id = st.journal_id.default_debit_account_id.id
acc_cur = ((st_line.amount<=0) and st.journal_id.default_debit_account_id) or st_line.account_id
context.update({
'res.currency.compute.account': acc_cur,
})
amount = res_currency_obj.compute(cr, uid, st.currency.id,
company_currency_id, st_line.amount, context=context,
account=acc_cur)
company_currency_id, st_line.amount, context=context)
val = {
'name': st_line.name,
@ -245,20 +247,15 @@ class account_bank_statement(osv.osv):
'analytic_account_id': st_line.analytic_account_id and st_line.analytic_account_id.id or False
}
amount = res_currency_obj.compute(cr, uid, st.currency.id,
company_currency_id, st_line.amount, context=context,
account=acc_cur)
if st.currency.id <> company_currency_id:
amount_cur = res_currency_obj.compute(cr, uid, company_currency_id,
st.currency.id, amount, context=context,
account=acc_cur)
st.currency.id, amount, context=context)
val['amount_currency'] = -amount_cur
if st_line.account_id and st_line.account_id.currency_id and st_line.account_id.currency_id.id <> company_currency_id:
val['currency_id'] = st_line.account_id.currency_id.id
amount_cur = res_currency_obj.compute(cr, uid, company_currency_id,
st_line.account_id.currency_id.id, amount, context=context,
account=acc_cur)
st_line.account_id.currency_id.id, amount, context=context)
val['amount_currency'] = -amount_cur
move_line_id = account_move_line_obj.create(cr, uid, val, context=context)

View File

@ -289,7 +289,12 @@ class account_move_line(osv.osv):
acc = account
if s>0:
acc = acc1
v = currency_obj.compute(cr, uid, account.company_id.currency_id.id, data['currency_id'], s, account=acc, account_invert=True)
compute_ctx = context.copy()
compute_ctx.update({
'res.currency.compute.account': acc,
'res.currency.compute.account_invert': True,
})
v = currency_obj.compute(cr, uid, account.company_id.currency_id.id, data['currency_id'], s, context=compute_ctx)
data['amount_currency'] = v
return data
@ -556,8 +561,11 @@ class account_move_line(osv.osv):
if (amount>0) and journal:
x = journal_obj.browse(cr, uid, journal).default_credit_account_id
if x: acc = x
context.update({'date': date})
v = currency_obj.compute(cr, uid, currency_id, acc.company_id.currency_id.id, amount, account=acc, context=context)
context.update({
'date': date,
'res.currency.compute.account': acc,
})
v = currency_obj.compute(cr, uid, currency_id, acc.company_id.currency_id.id, amount, context=context)
result['value'] = {
'debit': v > 0 and v or 0.0,
'credit': v < 0 and -v or 0.0
@ -955,7 +963,6 @@ class account_move_line(osv.osv):
if field in widths:
attrs.append('width="'+str(widths[field])+'"')
attrs.append('string="'+field_it[2]+'"')
attrs.append("invisible=\"context.get('visible_id') not in %s\"" % (fields.get(field)))
xml += '''<field name="%s" %s/>\n''' % (field,' '.join(attrs))

View File

@ -411,7 +411,7 @@
<group colspan="4" col="6">
<field name="name" select="1"/>
<field name="code" select="1"/>
<field name="type" on_change="onchange_type(type, currency)"/>
<field name="type" on_change="onchange_type(type, currency, context)"/>
</group>
<notebook colspan="4">
<page string="General Information">

View File

@ -1313,7 +1313,6 @@ class account_invoice_line(osv.osv):
if context is None:
context = {}
company_id = context.get('company_id',False)
tax_obj = self.pool.get('account.tax')
if not partner_id:
raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
if not product:

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2010 OpenERP s.a. (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import osv
"""Inherit res.currency to handle accounting date values when converting currencies"""
class res_currency_account(osv.osv):
_inherit = "res.currency"
def _get_conversion_rate(self, cr, uid, from_currency, to_currency, context=None):
if context is None:
context = {}
rate = super(res_currency_account, self)._get_conversion_rate(cr, uid, from_currency, to_currency, context=context)
account = context.get('res.currency.compute.account')
account_invert = context.get('res.currency.compute.account_invert')
if account and account.currency_mode == 'average' and account.currency_id:
query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
cr.execute('select sum(debit-credit),sum(amount_currency) from account_move_line l ' \
'where l.currency_id=%s and l.account_id=%s and '+query, (account.currency_id.id,account.id,))
tot1,tot2 = cr.fetchone()
if tot2 and not account_invert:
rate = float(tot1)/float(tot2)
elif tot1 and account_invert:
rate = float(tot2)/float(tot1)
return rate
res_currency_account()

View File

@ -65,8 +65,6 @@ class crossovered_analytic(report_sxw.rml_parse):
def _ref_lines(self,form):
result = []
res = {}
acc_id = []
final = []
acc_pool = self.pool.get('account.analytic.account')
line_pool = self.pool.get('account.analytic.line')
@ -128,7 +126,6 @@ class crossovered_analytic(report_sxw.rml_parse):
line_pool = self.pool.get('account.analytic.line')
acc_id = []
final = []
child_ids = []
self.list_ids = []
self.final_list = self.find_children(ids)

View File

@ -401,7 +401,7 @@ class account_voucher(osv.osv):
return default
def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, context=None):
def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None):
"""price
Returns a dict that contains new values and context
@ -415,6 +415,9 @@ class account_voucher(osv.osv):
context = {}
if not journal_id:
return {}
context_multi_currency = context.copy()
if date:
context_multi_currency.update({'date': date})
line_pool = self.pool.get('account.voucher.line')
line_ids = ids and line_pool.search(cr, uid, [('voucher_id', '=', ids[0])]) or False
@ -476,9 +479,9 @@ class account_voucher(osv.osv):
company_currency = journal.company_id.currency_id.id
if company_currency != currency_id and ttype == 'payment':
total_debit = currency_pool.compute(cr, uid, currency_id, company_currency, total_debit)
total_debit = currency_pool.compute(cr, uid, currency_id, company_currency, total_debit, context=context_multi_currency)
elif company_currency != currency_id and ttype == 'receipt':
total_credit = currency_pool.compute(cr, uid, currency_id, company_currency, total_credit)
total_credit = currency_pool.compute(cr, uid, currency_id, company_currency, total_credit, context=context_multi_currency)
for line in moves:
if line.credit and line.reconcile_partial_id and ttype == 'receipt':
@ -500,18 +503,18 @@ class account_voucher(osv.osv):
'type': line.credit and 'dr' or 'cr',
'move_line_id':line.id,
'account_id':line.account_id.id,
'amount_original':currency_pool.compute(cr, uid, company_currency, currency_id, original_amount),
'amount_original':currency_pool.compute(cr, uid, company_currency, currency_id, original_amount, context=context_multi_currency),
'date_original':line.date,
'date_due':line.date_maturity,
'amount_unreconciled':currency_pool.compute(cr, uid, company_currency, currency_id, line.amount_unreconciled)
'amount_unreconciled':currency_pool.compute(cr, uid, company_currency, currency_id, line.amount_unreconciled, context=context_multi_currency)
}
if line.credit:
amount = min(line.amount_unreconciled, total_debit)
rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount)
rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount, context=context_multi_currency)
total_debit -= amount
else:
amount = min(line.amount_unreconciled, total_credit)
rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount)
rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount, context=context_multi_currency)
total_credit -= amount
default['value']['line_ids'].append(rs)
@ -528,7 +531,7 @@ class account_voucher(osv.osv):
return default
def onchange_date(self, cr, user, ids, date, context=None):
def onchange_date(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None):
"""
@param date: latest value from user input for field date
@param args: other arguments
@ -536,14 +539,13 @@ class account_voucher(osv.osv):
@return: Returns a dict which contains new values, and context
"""
period_pool = self.pool.get('account.period')
pids = period_pool.search(cr, user, [('date_start', '<=', date), ('date_stop', '>=', date)])
if not pids:
return {}
return {
'value':{
'period_id':pids[0]
}
}
res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=context)
pids = period_pool.search(cr, uid, [('date_start', '<=', date), ('date_stop', '>=', date)])
if pids:
if not 'value' in res:
res['value'] = {}
res['value'].update({'period_id':pids[0]})
return res
def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, context=None):
if not journal_id:
@ -642,6 +644,9 @@ class account_voucher(osv.osv):
for inv in self.browse(cr, uid, ids, context=context):
if inv.move_id:
continue
context_multi_currency = context.copy()
context_multi_currency.update({'date': inv.date})
if inv.number:
name = inv.number
elif inv.journal_id.sequence_id:
@ -671,9 +676,9 @@ class account_voucher(osv.osv):
# TODO: is there any other alternative then the voucher type ??
# -for sale, purchase we have but for the payment and receipt we do not have as based on the bank/cash journal we can not know its payment or receipt
if inv.type in ('purchase', 'payment'):
credit = currency_pool.compute(cr, uid, current_currency, company_currency, inv.amount)
credit = currency_pool.compute(cr, uid, current_currency, company_currency, inv.amount, context=context_multi_currency)
elif inv.type in ('sale', 'receipt'):
debit = currency_pool.compute(cr, uid, current_currency, company_currency, inv.amount)
debit = currency_pool.compute(cr, uid, current_currency, company_currency, inv.amount, context=context_multi_currency)
if debit < 0:
credit = -debit
debit = 0.0
@ -701,14 +706,14 @@ class account_voucher(osv.osv):
rec_list_ids = []
line_total = debit - credit
if inv.type == 'sale':
line_total = line_total - currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount)
line_total = line_total - currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount, context=context_multi_currency)
elif inv.type == 'purchase':
line_total = line_total + currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount)
line_total = line_total + currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount, context=context_multi_currency)
for line in inv.line_ids:
if not line.amount:
continue
amount = currency_pool.compute(cr, uid, current_currency, company_currency, line.untax_amount or line.amount)
amount = currency_pool.compute(cr, uid, current_currency, company_currency, line.untax_amount or line.amount, context=context_multi_currency)
move_line = {
'journal_id': inv.journal_id.id,
'period_id': inv.period_id.id,
@ -770,7 +775,7 @@ class account_voucher(osv.osv):
'date': inv.date,
'credit': diff > 0 and diff or 0.0,
'debit': diff < 0 and -diff or 0.0,
'amount_currency': company_currency <> current_currency and currency_pool.compute(cr, uid, company_currency, current_currency, diff * -1) or 0.0,
'amount_currency': company_currency <> current_currency and currency_pool.compute(cr, uid, company_currency, current_currency, diff * -1, context=context_multi_currency) or 0.0,
'currency_id': company_currency <> current_currency and current_currency or False,
}
@ -1004,4 +1009,4 @@ class account_bank_statement_line(osv.osv):
account_bank_statement_line()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -29,9 +29,10 @@
<form string="Accounting Voucher">
<group col="6" colspan="4">
<field name="partner_id" required="1" on_change="onchange_journal_voucher(line_ids, tax_id, amount, partner_id, journal_id, type)"/>
<field name="date" on_change="onchange_date(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="journal_id" widget="selection" select="1" on_change="onchange_journal_voucher(line_ids, tax_id, amount, partner_id, journal_id, type)"/>
<field name="type" required="1"/>
<field name="name" colspan="4"/>
<field name="name" colspan="2"/>
<field name="reference" select="1"/>
<field name="account_id" widget="selection" invisible="True"/>
</group>
@ -54,7 +55,6 @@
<group col="2" colspan="1" >
<separator string="Other Information" colspan="2"/>
<field name="number"/>
<field name="date" on_change="onchange_date(date)"/>
<field name="currency_id" invisible="True"/>
</group>
<group col="4" colspan="1" attrs="{'invisible':[('type','in',['payment', 'receipt', False])]}">

View File

@ -42,7 +42,7 @@
import netsvc
vals = {}
journal_id = self.default_get(cr, uid, ['journal_id']).get('journal_id',None)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), journal_id, 0.0, 1, ttype='receipt')
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), journal_id, 0.0, 1, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 450.0,

View File

@ -50,7 +50,7 @@
import netsvc
vals = {}
journal_id = self.default_get(cr, uid, ['journal_id']).get('journal_id',None)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), journal_id, 0.0, 1, ttype='receipt')
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), journal_id, 0.0, 1, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 30000.0,

View File

@ -88,15 +88,16 @@
<field name="arch" type="xml">
<form string="Bill Payment">
<group col="6" colspan="4">
<field name="partner_id" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)" context="{'invoice_currency':currency_id}" string="Supplier"/>
<field name="partner_id" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)" context="{'invoice_currency':currency_id}" string="Supplier"/>
<field name="date" select="1" on_change="onchange_date(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="journal_id"
domain="[('type','in',['bank', 'cash'])]"
widget="selection" select="1"
on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)"
on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)"
string="Payment Method"/>
<field name="amount" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)"/>
<field name="amount" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="reference" select="1" string="Payment Ref"/>
<field name="name" colspan="4"/>
<field name="name" colspan="2"/>
<field name="account_id"
widget="selection"
invisible="True"/>
@ -134,7 +135,6 @@
</group>
<group col="2" colspan="1">
<separator string="Other Information" colspan="2"/>
<field name="date" select="1" on_change="onchange_date(date)"/>
<field name="currency_id" invisible="True"/>
<field name="number"/>
</group>
@ -151,15 +151,16 @@
<field name="arch" type="xml">
<form string="Bill Payment">
<group col="6" colspan="4">
<field name="partner_id" domain="[('supplier','=',True)]" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)" context="{'invoice_currency':currency_id}" string="Supplier"/>
<field name="partner_id" domain="[('supplier','=',True)]" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)" context="{'invoice_currency':currency_id}" string="Supplier"/>
<field name="date" select="1" on_change="onchange_date(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="journal_id"
domain="[('type','in',['bank', 'cash'])]"
widget="selection" select="1"
on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)"
on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)"
string="Payment Method"/>
<field name="amount" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)"/>
<field name="amount" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="reference" select="1" string="Payment Ref"/>
<field name="name" colspan="4"/>
<field name="name" colspan="2"/>
<field name="account_id"
widget="selection"
invisible="True"/>
@ -211,7 +212,6 @@
groups="analytic.group_analytic_accounting"/>
</group>
<group col="4" colspan="1">
<field name="date" select="1" on_change="onchange_date(date)"/>
<field name="currency_id" invisible="True"/>
<field name="number"/>
</group>
@ -285,17 +285,18 @@
<field name="arch" type="xml">
<form string="Customer Payment">
<group col="6" colspan="4">
<field name="partner_id" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)" string="Customer"/>
<field name="partner_id" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)" string="Customer"/>
<field name="date" select="1" on_change="onchange_date(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="journal_id"
domain="[('type','in',['bank', 'cash'])]"
widget="selection" select="1"
on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)"
on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)"
string="Payment Method"/>
<field name="amount"
string="Paid Amount"
on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)"/>
on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="reference" select="1" string="Payment Ref"/>
<field name="name" colspan="4"/>
<field name="name" colspan="2"/>
<field name="account_id"
widget="selection"
invisible="True"/>
@ -348,7 +349,6 @@
</group>
<separator string="Other Information" colspan="2"/>
<group col="4" colspan="1">
<field name="date" select="1" on_change="onchange_date(date)"/>
<field name="currency_id" invisible="True"/>
<field name="number"/>
</group>

View File

@ -96,11 +96,11 @@
<field name="arch" type="xml">
<form string="Sales Receipt">
<group col="6" colspan="4">
<field name="partner_id" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)" string="Customer"/>
<field name="partner_id" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)" string="Customer"/>
<field name="date" on_change="onchange_date(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="journal_id" domain="[('type','in',['sale','sale_refund'])]" widget="selection" on_change="onchange_journal(journal_id, line_cr_ids, tax_id, partner_id)"/>
<field name="number"/>
<field name="name" colspan="4"/>
<field name="date" on_change="onchange_date(date)"/>
<field name="type" invisible="True"/>
<field name="currency_id" invisible="True" nolabel="1"/>
</group>
@ -222,10 +222,11 @@
<field name="arch" type="xml">
<form string="Supplier Voucher">
<group col="6" colspan="4">
<field name="partner_id" domain="[('supplier','=',True)]" required="1" string="Supplier" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type)"/>
<field name="partner_id" domain="[('supplier','=',True)]" required="1" string="Supplier" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="date" string="Bill Date" select="1" on_change="onchange_date(partner_id, journal_id, amount, currency_id, type, date)"/>
<field name="journal_id" domain="[('type','in',['purchase','purchase_refund'])]" widget="selection" select="1" on_change="onchange_journal(journal_id, line_dr_ids, tax_id, partner_id)"/>
<field name="number"/>
<field name="name" colspan="4"/>
<field name="name" colspan="2"/>
<field name="reference" select="1"/>
<field name="account_id" domain="[('type','=','other')]" invisible="True"/>
<field name="type" invisible="True"/>
@ -248,7 +249,6 @@
<group col="2" colspan="1">
<group col="2" colspan="1">
<separator string="Payment Terms" colspan="2"/>
<field name="date" string="Bill Date" select="1" on_change="onchange_date(date)"/>
<field name="date_due"/>
</group>
<group col="4" colspan="1">

View File

@ -106,14 +106,16 @@ class audittrail_rule(osv.osv):
"""
obj_action = self.pool.get('ir.actions.act_window')
val_obj = self.pool.get('ir.values')
value=''
#start Loop
for thisrule in self.browse(cr, uid, ids):
if thisrule.id in self.__functions:
for function in self.__functions[thisrule.id]:
setattr(function[0], function[1], function[2])
w_id = obj_action.search(cr, uid, [('name', '=', 'View Log'), ('res_model', '=', 'audittrail.log'), ('src_model', '=', thisrule.object_id.model)])
obj_action.unlink(cr, uid, w_id)
value = "ir.actions.act_window" + ',' + str(w_id[0])
if w_id:
obj_action.unlink(cr, uid, w_id)
value = "ir.actions.act_window" + ',' + str(w_id[0])
val_id = val_obj.search(cr, uid, [('model', '=', thisrule.object_id.model), ('value', '=', value)])
if val_id:
res = ir.ir_del(cr, uid, val_id[0])

View File

@ -29,6 +29,11 @@ import re
import time
import tools
def get_datetime(date_field):
return datetime.strptime(date_field[:19], '%Y-%m-%d %H:%M:%S')
class base_action_rule(osv.osv):
""" Base Action Rules """
@ -115,6 +120,7 @@ the rule to mark CC(mail to any other person defined in actions)."),
help="Use a python expression to specify the right field on which one than we will use for the 'From' field of the header"),
'act_email_to' : fields.char('Email To', size=64, required=False,
help="Use a python expression to specify the right field on which one than we will use for the 'To' field of the header"),
'last_run': fields.datetime('Last Run', readonly=1),
}
_defaults = {
@ -145,7 +151,7 @@ the rule to mark CC(mail to any other person defined in actions)."),
# Searching for action rules
cr.execute("SELECT model.model, rule.id FROM base_action_rule rule \
LEFT JOIN ir_model model on (model.id = rule.model_id) \
where active")
WHERE active")
res = cr.fetchall()
# Check if any rule matching with current object
for obj_name, rule_id in res:
@ -153,7 +159,10 @@ the rule to mark CC(mail to any other person defined in actions)."),
continue
else:
obj = self.pool.get(obj_name)
self._action(cr, uid, [rule_id], obj.browse(cr, uid, ids, context=context), context=context)
# If the rule doesn't involve a time condition, run it immediately
# Otherwise we let the scheduler run the action
if self.browse(cr, uid, rule_id, context=context).trg_date_type == 'none':
self._action(cr, uid, [rule_id], obj.browse(cr, uid, ids, context=context), context=context)
return True
def _create(self, old_create, model, context=None):
@ -206,8 +215,50 @@ the rule to mark CC(mail to any other person defined in actions)."),
"""
rule_pool = self.pool.get('base.action.rule')
rule_ids = rule_pool.search(cr, uid, [], context=context)
return self._register_hook(cr, uid, rule_ids, context=context)
self._register_hook(cr, uid, rule_ids, context=context)
rules = self.browse(cr, uid, rule_ids, context=context)
for rule in rules:
model = rule.model_id.model
model_pool = self.pool.get(model)
last_run = False
if rule.last_run:
last_run = get_datetime(rule.last_run)
now = datetime.now()
for obj_id in model_pool.search(cr, uid, [], context=context):
obj = model_pool.browse(cr, uid, obj_id, context=context)
# Calculate when this action should next occur for this object
base = False
if rule.trg_date_type=='create' and hasattr(obj, 'create_date'):
base = obj.create_date
elif (rule.trg_date_type=='action_last'
and hasattr(obj, 'create_date')):
if hasattr(obj, 'date_action_last') and obj.date_action_last:
base = obj.date_action_last
else:
base = obj.create_date
elif (rule.trg_date_type=='deadline'
and hasattr(obj, 'date_deadline')
and obj.date_deadline):
base = obj.date_deadline
elif (rule.trg_date_type=='date'
and hasattr(obj, 'date')
and obj.date):
base = obj.date
if base:
fnct = {
'minutes': lambda interval: timedelta(minutes=interval),
'day': lambda interval: timedelta(days=interval),
'hour': lambda interval: timedelta(hours=interval),
'month': lambda interval: timedelta(months=interval),
}
base = get_datetime(base)
delay = fnct[rule.trg_date_range_type](rule.trg_date_range)
action_date = base + delay
if (not last_run or (last_run <= action_date < now)):
self._action(cr, uid, [rule.id], [obj], context=context)
rule_pool.write(cr, uid, [rule.id], {'last_run': now},
context=context)
def format_body(self, body):
""" Foramat Action rule's body
@ -391,7 +442,6 @@ the rule to mark CC(mail to any other person defined in actions)."),
if context is None:
context = {}
context.update({'action': True})
if not scrit:
scrit = []
@ -403,41 +453,6 @@ the rule to mark CC(mail to any other person defined in actions)."),
if not ok:
continue
base = False
if action.trg_date_type=='create' and hasattr(obj, 'create_date'):
base = datetime.strptime(obj.create_date[:19], '%Y-%m-%d %H:%M:%S')
elif action.trg_date_type=='action_last' and hasattr(obj, 'create_date'):
if hasattr(obj, 'date_action_last') and obj.date_action_last:
base = datetime.strptime(obj.date_action_last, '%Y-%m-%d %H:%M:%S')
else:
base = datetime.strptime(obj.create_date[:19], '%Y-%m-%d %H:%M:%S')
elif action.trg_date_type=='deadline' and hasattr(obj, 'date_deadline') \
and obj.date_deadline:
base = datetime.strptime(obj.date_deadline, '%Y-%m-%d %H:%M:%S')
elif action.trg_date_type=='date' and hasattr(obj, 'date') and obj.date:
base = datetime.strptime(obj.date, '%Y-%m-%d %H:%M:%S')
if base:
fnct = {
'minutes': lambda interval: timedelta(minutes=interval),
'day': lambda interval: timedelta(days=interval),
'hour': lambda interval: timedelta(hours=interval),
'month': lambda interval: timedelta(months=interval),
}
d = base + fnct[action.trg_date_range_type](action.trg_date_range)
dt = d.strftime('%Y-%m-%d %H:%M:%S')
ok = False
if hasattr(obj, 'date_action_last') and hasattr(obj, 'date_action_next'):
ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \
((not obj.date_action_next) or \
(dt >= obj.date_action_next and \
obj.date_action_last < obj.date_action_next))
if not ok:
if not obj.date_action_next or dt < obj.date_action_next:
obj.date_action_next = dt
model_obj.write(cr, uid, [obj.id], {'date_action_next': dt}, context)
else:
ok = action.trg_date_type == 'none'
if ok:
self.do_action(cr, uid, action, model_obj, obj, context)
break

View File

@ -1024,45 +1024,43 @@ class calendar_event(osv.osv):
qry = "UPDATE \"%s\" set rrule_type=%%s " % self._table
qry_args = [ rrule_type, ]
new_val = val.copy()
for k, v in val.items():
if val['freq'] == 'weekly' and val.get('byday'):
for day in val['byday'].split(','):
new_val[day] = True
val.pop('byday')
if rrule_type == 'custom':
new_val = val.copy()
for k, v in val.items():
if val['freq'] == 'weekly' and val.get('byday'):
for day in val['byday'].split(','):
new_val[day] = True
val.pop('byday')
if val.get('until'):
until = parser.parse(''.join((re.compile('\d')).findall(val.get('until'))))
new_val['end_date'] = until.strftime('%Y-%m-%d')
val.pop('until')
new_val.pop('until')
if val.get('until'):
until = parser.parse(''.join((re.compile('\d')).findall(val.get('until'))))
new_val['end_date'] = until.strftime('%Y-%m-%d')
val.pop('until')
new_val.pop('until')
if val.get('bymonthday'):
new_val['day'] = val.get('bymonthday')
val.pop('bymonthday')
new_val['select1'] = 'date'
new_val.pop('bymonthday')
if val.get('bymonthday'):
new_val['day'] = val.get('bymonthday')
val.pop('bymonthday')
new_val['select1'] = 'date'
new_val.pop('bymonthday')
if val.get('byday'):
d = val.get('byday')
if '-' in d:
new_val['byday'] = d[:2]
new_val['week_list'] = d[2:4].upper()
else:
new_val['byday'] = d[:1]
new_val['week_list'] = d[1:3].upper()
new_val['select1'] = 'day'
if val.get('byday'):
d = val.get('byday')
if '-' in d:
new_val['byday'] = d[:2]
new_val['week_list'] = d[2:4].upper()
else:
new_val['byday'] = d[:1]
new_val['week_list'] = d[1:3].upper()
new_val['select1'] = 'day'
if val.get('bymonth'):
new_val['month_list'] = val.get('bymonth')
val.pop('bymonth')
new_val.pop('bymonth')
if val.get('bymonth'):
new_val['month_list'] = val.get('bymonth')
val.pop('bymonth')
new_val.pop('bymonth')
for k, v in new_val.items():
qry += ", %s=%%s" % k
qry_args.append(v)
for k, v in new_val.items():
qry += ", %s=%%s" % k
qry_args.append(v)
qry = qry + " where id=%s"
qry_args.append(id)
@ -1082,10 +1080,12 @@ class calendar_event(osv.osv):
for datas in self.read(cr, uid, ids, context=context):
event = datas['id']
if datas.get('rrule_type'):
if datas['rrule_type']=='daily_working':
datas.update({'rrule_type': 'weekly'})
if datas.get('rrule_type') == 'none':
result[event] = False
cr.execute("UPDATE %s set exrule=Null where id=%%s" % self._table,( event,))
elif datas.get('rrule_type') == 'custom':
if datas.get('rrule_type') :
if datas.get('interval', 0) < 0:
raise osv.except_osv(_('Warning!'), _('Interval can not be Negative'))
if datas.get('count', 0) < 0:
@ -1132,7 +1132,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=-1SU'),
'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \
('weekly', 'Weekly'), ('monthly', 'Monthly'), \
('yearly', 'Yearly'), ('custom', 'Custom')],
('yearly', 'Yearly'),],
'Recurrency', states={'done': [('readonly', True)]},
help="Let the event automatically repeat at that interval"),
'alarm_id': fields.many2one('res.alarm', 'Alarm', states={'done': [('readonly', True)]},
@ -1144,16 +1144,15 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with Organizer Attribure of VEvent.
'organizer_id': fields.many2one('res.users', 'Organizer', states={'done': [('readonly', True)]}),
'freq': fields.selection([('None', 'No Repeat'), \
('hourly', 'Hours'), \
('daily', 'Days'), \
('weekly', 'Weeks'), \
('monthly', 'Months'), \
('yearly', 'Years'), \
('secondly', 'Seconds'), \
('minutely', 'Minutes') ], 'Frequency'),
'interval': fields.integer('Interval', help="Repeat every x"),
'count': fields.integer('Count', help="Repeat max that times"),
'freq': fields.selection([('None', 'No Repeat'),
('hourly', 'Hours'),
('daily', 'Days'),
('weekly', 'Weeks'),
('monthly', 'Months'),
('yearly', 'Years'), ], 'Frequency'),
'interval': fields.integer('Interval', help="Repeat every (Days/Week/Month/Year)"),
'count': fields.integer('Repeat', help="Repeat x times"),
'mo': fields.boolean('Mon'),
'tu': fields.boolean('Tue'),
'we': fields.boolean('Wed'),
@ -1161,7 +1160,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'fr': fields.boolean('Fri'),
'sa': fields.boolean('Sat'),
'su': fields.boolean('Sun'),
'select1': fields.selection([('date', 'Date of month'), \
'select1': fields.selection([('date', 'Date of month'),
('day', 'Day of month')], 'Option'),
'day': fields.integer('Date of month'),
'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), \
@ -1177,7 +1176,9 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'event_id', 'attendee_id', 'Attendees'),
'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
'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.")
true, it will allow you to hide the event alarm information without removing it."),
'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"),
'edit_all': fields.boolean('Edit All', help="Edit all Occurrences of recurrent Meeting."),
}
def default_organizer(self, cr, uid, context=None):
user_pool = self.pool.get('res.users')
@ -1199,45 +1200,15 @@ true, it will allow you to hide the event alarm information without removing it.
'organizer': default_organizer,
}
def open_event(self, cr, uid, ids, context=None):
"""
Open Event From for Editing
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of events IDs
@param context: A standard dictionary for contextual values
@return: Dictionary value which open Crm Meeting form.
"""
if context is None:
def onchange_edit_all(self, cr, uid, ids, rrule_type,edit_all, context=None):
if not context:
context = {}
data_obj = self.pool.get('ir.model.data')
value = {}
id2 = data_obj._get_id(cr, uid, 'base_calendar', 'event_form_view')
id3 = data_obj._get_id(cr, uid, 'base_calendar', 'event_tree_view')
id4 = data_obj._get_id(cr, uid, 'base_calendar', 'event_calendar_view')
if id2:
id2 = data_obj.browse(cr, uid, id2, context=context).res_id
if id3:
id3 = data_obj.browse(cr, uid, id3, context=context).res_id
if id4:
id4 = data_obj.browse(cr, uid, id4, context=context).res_id
for id in ids:
value = {
'name': _('Event'),
'view_type': 'form',
'view_mode': 'form,tree',
'res_model': 'calendar.event',
'view_id': False,
'views': [(id2, 'form'), (id3, 'tree'), (id4, 'calendar')],
'type': 'ir.actions.act_window',
'res_id': base_calendar_id2real_id(id),
'nodestroy': True
}
return value
if edit_all and rrule_type:
for id in ids:
base_calendar_id2real_id(id)
return value
def modify_all(self, cr, uid, event_ids, defaults, context=None, *args):
"""
@ -1367,15 +1338,13 @@ true, it will allow you to hide the event alarm information without removing it.
weekstring = ''
monthstring = ''
yearstring = ''
freq = datas.get('freq')
if freq == 'None':
freq=datas.get('rrule_type')
if freq == 'none':
return ''
interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or ''
if freq == 'weekly':
byday = map(lambda x: x.upper(), filter(lambda x: datas.get(x) and x in weekdays, datas))
if byday:
weekstring = ';BYDAY=' + ','.join(byday)
@ -1388,16 +1357,7 @@ true, it will allow you to hide the event alarm information without removing it.
elif datas.get('select1')=='date':
monthstring = ';BYMONTHDAY=' + str(datas.get('day'))
elif freq == 'yearly':
if datas.get('select1')=='date' and (datas.get('day') < 1 or datas.get('day') > 31):
raise osv.except_osv(_('Error!'), ("Please select proper Day of month"))
bymonth = ';BYMONTH=' + str(datas.get('month_list'))
if datas.get('select1')=='day':
bystring = ';BYDAY=' + datas.get('byday') + datas.get('week_list')
elif datas.get('select1')=='date':
bystring = ';BYMONTHDAY=' + str(datas.get('day'))
yearstring = bymonth + bystring
if datas.get('end_date'):
datas['end_date'] = ''.join((re.compile('\d')).findall(datas.get('end_date'))) + 'T235959Z'
enddate = (datas.get('count') and (';COUNT=' + str(datas.get('count'))) or '') +\

View File

@ -241,62 +241,9 @@
<field name="alarm_id" string="Reminder"
widget="selection" />
<group colspan="2" col="4" >
<field name="rrule_type" string="Recurrency"
colspan="1" attrs="{'readonly':[('recurrent_uid','!=',False)]}"/>
<button string="Edit All"
help="Edit all Ourrences of recurrent Events"
attrs="{'invisible':[('rrule_type','in', ('none', False))]}"
name="open_event" icon="gtk-edit"
type="object" />
<button string="Exclude range" groups="base.group_extended"
help="Add Exception Rule"
name="%(base_calendar.action_base_calendar_set_exrule)d" icon="gtk-zoom-out" type="action"
context="{'model' : 'calendar.event'}"
attrs="{'invisible':[('rrule_type','in', ('none', False))]}"/>
</group>
</group>
<group col="4" colspan="4" name="rrule" attrs="{'invisible': [('rrule_type','!=','custom')]}">
<separator string="Select data for Custom Rule" colspan="8"/>
<group col="8" colspan="4">
<field name="interval" />
<field name="freq" />
<field name="count" />
<field name="end_date" />
</group>
<group col="14" colspan="4" name="Select weekdays"
attrs="{'invisible' : [('freq','!=','weekly')]}">
<field name="mo" colspan="1" />
<field name="tu" colspan="1" />
<field name="we" colspan="1" />
<field name="th" colspan="1" />
<field name="fr" colspan="1" />
<field name="sa" colspan="1" />
<field name="su" colspan="1" />
<newline />
</group>
<group col="10" colspan="4"
attrs="{'invisible' : [('freq','!=','monthly'), ('freq','!=','yearly')]}">
<group col="2" colspan="1">
<field name="select1" />
</group>
<group col="2" colspan="1"
attrs="{'invisible' : [('select1','=','day')]}">
<field name="day"
attrs="{'required' : [('select1','=','date')]}" />
</group>
<group col="3" colspan="1"
attrs="{'invisible' : [('select1','=','date')]}">
<field name="byday" string="The"
attrs="{'required' : [('select1','=','day')]}" />
<field name="week_list" nolabel="1"
attrs="{'required' : [('select1','=','day')]}" />
</group>
<group col="1" colspan="1"
attrs="{'invisible' : [('freq','!=','yearly')]}">
<field name="month_list" string="of"
colspan="1"
attrs="{'required' : [('freq','=','yearly')]}" />
</group>
<field name="recurrency"/>
<field name="edit_all" attrs="{'invisible':[('recurrency','=', False)]}"
on_change="onchange_edit_all(rrule_type,edit_all)"/>
</group>
</group>
<notebook colspan="4">
@ -397,6 +344,57 @@
</form>
</field>
</page>
<page string="Recurrency Option" attrs="{'invisible':[('recurrency','=',False)]}">
<group col="4" colspan="4" name="rrule">
<group col="4" colspan="4">
<field name="rrule_type" string="Recurrency period"
attrs="{'readonly':[('recurrent_uid','!=',False)]}" />
<label string="" colspan="2"/>
<separator string="End of recurrency" colspan="4"/>
<field name="count" attrs="{'readonly': [('end_date','!=',False)]}"/>
<label string=" " colspan="2" />
<newline />
<field name="end_date" attrs="{'readonly': [('count','!=',False)]}"/>
<newline />
<separator string="Repeat interval" colspan="4"/>
<field name="interval" /> <label string="" colspan="2"/>
</group>
<group col="8" colspan="4" name="Select weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}">
<separator string="Choose day where repeat the meeting" colspan="8"/>
<field name="mo" colspan="1" />
<field name="tu" colspan="1" />
<field name="we" colspan="1" />
<field name="th" colspan="1" />
<newline/>
<field name="fr" colspan="1" />
<field name="sa" colspan="1" />
<field name="su" colspan="1" />
<newline />
</group>
<group col="10" colspan="4"
attrs="{'invisible' : [('rrule_type','!=','monthly')]}">
<separator string="Choose day in the month where repeat the meeting" colspan="12"/>
<group col="2" colspan="1">
<field name="select1" />
</group>
<group col="2" colspan="1"
attrs="{'invisible' : [('select1','=','day')]}">
<field name="day"
attrs="{'required' : [('select1','=','date'), ('rrule_type','=','monthly')]}" />
</group>
<group col="3" colspan="1"
attrs="{'invisible' : [('select1','=','date'), ('rrule_type','=','monthly')]}">
<field name="byday" string="The"
attrs="{'required' : [('select1','=','day'), ('rrule_type','=','monthly')]}" />
<field name="week_list" nolabel="1"
attrs="{'required' : [('select1','=','day'), ('rrule_type','=','monthly')]}" />
</group>
</group>
</group>
</page>
</notebook>
</form>
</field>

View File

@ -15,7 +15,7 @@
Now I will set recurrence for this event to occur monday and friday of week
-
!python {model: calendar.event}: |
self.write(cr, uid, [ref("calendar_event_technicalpresentation0")], {'fr': 1, 'mo': 1, 'interval': 1, 'freq': 'weekly', 'rrule_type': 'custom'})
self.write(cr, uid, [ref("calendar_event_technicalpresentation0")], {'fr': 1, 'mo': 1, 'interval': 1, 'rrule_type': 'weekly'})
- |
In order to check that recurrent events are views successfully in calendar view,
I will open calendar view of events

View File

@ -38,8 +38,6 @@ class base_contact_installer(osv.osv_memory):
obj = self.pool.get("base.contact.installer").browse(cr, uid, uid, context=context)
if obj.migrate:
cr.execute("""DROP TRIGGER IF EXISTS contactjob on res_partner_contact;
DROP LANGUAGE IF EXISTS plpgsql CASCADE;
CREATE LANGUAGE plpgsql ;
CREATE OR REPLACE FUNCTION add_to_job() RETURNS TRIGGER AS $contactjob$
DECLARE
new_name varchar;
@ -57,7 +55,7 @@ class base_contact_installer(osv.osv_memory):
cr.execute("INSERT into res_partner_contact (name, title, email, first_name, website) (SELECT coalesce(name, 'Noname'), title, email, function , to_char(id, '99999999') from res_partner_address)")
cr.execute("DROP TRIGGER IF EXISTS contactjob on res_partner_contact")
cr.execute("DROP LANGUAGE IF EXISTS plpgsql CASCADE;")
cr.execute("DROP FUNCTION IF EXISTS add_to_job()")
base_contact_installer()

View File

@ -25,6 +25,7 @@ from StringIO import StringIO
import base64
import pooler
import addons
import sys
class report_xml(osv.osv):
_inherit = 'ir.actions.report.xml'
@ -59,6 +60,8 @@ class report_xml(osv.osv):
def report_get(self, cr, uid, report_id, context=None):
report = self.browse(cr, uid, report_id, context=context)
reload(sys)
sys.setdefaultencoding( "latin-1" )
return {
'file_type' : report.report_type,
'report_sxw_content': report.report_sxw_content and base64.encodestring(report.report_sxw_content) or False,

View File

@ -579,8 +579,8 @@ class Calendar(CalDAV, osv.osv):
string="Type", size=64),
'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'),
'create_date': fields.datetime('Created Date', readonly=True),
'write_date': fields.datetime('Modifided Date', readonly=True),
'description': fields.text("description"),
'write_date': fields.datetime('Write Date', readonly=True),
'description': fields.text("Description"),
'calendar_color': fields.char('Color', size=20, help="For supporting clients, the color of the calendar entries"),
'calendar_order': fields.integer('Order', help="For supporting clients, the order of this folder among the calendars"),
'has_webcal': fields.boolean('WebCal', required=True, help="Also export a <name>.ics entry next to the calendar folder, with WebCal content."),
@ -818,6 +818,7 @@ class basic_calendar_fields(osv.osv):
_name = 'basic.calendar.fields'
_description = 'Calendar fields'
_order = 'name'
_columns = {
'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'),
@ -833,7 +834,7 @@ class basic_calendar_fields(osv.osv):
}
_defaults = {
'fn': lambda *a: 'field',
'fn': 'field',
}
_sql_constraints = [
@ -1164,11 +1165,16 @@ class Alarm(CalDAV, osv.osv_memory):
self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
for child in ical_data.getChildren():
if child.name.lower() == 'trigger':
seconds = child.value.seconds
days = child.value.days
diff = (days * 86400) + seconds
interval = 'days'
related = 'before'
if isinstance(child.value, timedelta):
seconds = child.value.seconds
days = child.value.days
diff = (days * 86400) + seconds
interval = 'days'
related = 'before'
elif isinstance(child.value, datetime):
# TODO
# remember, spec says this datetime is in UTC
raise NotImplementedError("we cannot parse absolute triggers")
if not seconds:
duration = abs(days)
related = days > 0 and 'after' or 'before'

View File

@ -82,8 +82,11 @@ class calendar_event_import(osv.osv_memory):
'msg': fields.text('', readonly=True),
}
def _get_msg(self, cr, uid, context):
return _('Import Sucessful')
_defaults = {
'msg':lambda *a:'Import Sucessful'
'msg': _get_msg,
}
calendar_event_import()

View File

@ -153,7 +153,7 @@
<field name="usage">menu</field>
<field name="view_id" ref="board_crm_form"/>
</record>
<menuitem id="board.menu_dasboard" name="Dashboard" sequence="0" parent="base.next_id_64"/>
<menuitem id="board.menu_dasboard" name="Dashboard" sequence="0" parent="base.next_id_64"/>
<menuitem
name="Sales Dashboard" parent="board.menu_dasboard"
action="open_board_crm"

View File

@ -47,7 +47,68 @@ AVAILABLE_PRIORITIES = [
class crm_case(object):
"""A simple python class to be used for common functions """
def _find_lost_stage(self, cr, uid, type, section_id):
return self._find_percent_stage(cr, uid, 0.0, type, section_id)
def _find_won_stage(self, cr, uid, type, section_id):
return self._find_percent_stage(cr, uid, 100.0, type, section_id)
def _find_percent_stage(self, cr, uid, percent, type, section_id):
"""
Return the first stage with a probability == percent
"""
stage_pool = self.pool.get('crm.case.stage')
if section_id :
ids = stage_pool.search(cr, uid, [("probability", '=', percent), ("type", 'like', type), ("section_ids", 'in', [section_id])])
else :
ids = stage_pool.search(cr, uid, [("probability", '=', percent), ("type", 'like', type)])
if ids:
return ids[0]
return False
def _find_first_stage(self, cr, uid, type, section_id):
"""
return the first stage that has a sequence number equal or higher than sequence
"""
stage_pool = self.pool.get('crm.case.stage')
if section_id :
ids = stage_pool.search(cr, uid, [("sequence", '>', 0), ("type", 'like', type), ("section_ids", 'in', [section_id])])
else :
ids = stage_pool.search(cr, uid, [("sequence", '>', 0), ("type", 'like', type)])
if ids:
stages = stage_pool.browse(cr, uid, ids)
stage_min = stages[0]
for stage in stages:
if stage_min.sequence > stage.sequence:
stage_min = stage
return stage_min.id
else :
return False
def onchange_stage_id(self, cr, uid, ids, stage_id, context={}):
""" @param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of stages IDs
@stage_id: change state id on run time """
if not stage_id:
return {'value':{}}
stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context)
if not stage.on_change:
return {'value':{}}
return {'value':{'probability': stage.probability}}
def _get_default_partner_address(self, cr, uid, context=None):
"""Gives id of default address for current user
@param self: The object pointer
@param cr: the current row, from the database cursor,
@ -149,6 +210,9 @@ class crm_case(object):
return False
next_seq = next_stage.sequence
if not current_seq :
current_seq = 0
if (abs(next_seq - current_seq)) >= 1:
return next_stage
else :
@ -157,17 +221,21 @@ class crm_case(object):
def stage_change(self, cr, uid, ids, context=None, order='sequence'):
if context is None:
context = {}
stage_pool = self.pool.get('crm.case.stage')
stage_type = context and context.get('stage_type','')
current_seq = False
next_stage_id = False
for case in self.browse(cr, uid, ids, context=context):
next_stage = False
data = {}
value = {}
if case.section_id.id :
domain = [('type', '=', stage_type),('section_ids', '=', case.section_id.id)]
else :
domain = [('type', '=', stage_type)]
domain = [('type', '=', stage_type),('section_ids', '=', case.section_id.id)]
if case.section_id and case.section_id.stage_ids:
domain.append(('id', 'in', map(lambda x: x.id, case.section_id.stage_ids)))
stages = stage_pool.search(cr, uid, domain, order=order)
current_seq = case.stage_id.sequence
@ -176,14 +244,16 @@ class crm_case(object):
index = stages.index(case.stage_id.id)
next_stage = self._find_next_stage(cr, uid, stages, index, current_seq, stage_pool, context=context)
if next_stage:
next_stage_id = next_stage.id
data = {'stage_id': next_stage.id}
value.update({'stage_id': next_stage.id})
if next_stage.on_change:
data.update({'probability': next_stage.probability})
self.write(cr, uid, [case.id], data, context=context)
value.update({'probability': next_stage.probability})
self.write(cr, uid, [case.id], value, context=context)
return next_stage_id
return next_stage_id #FIXME should return a list of all id
def stage_next(self, cr, uid, ids, context=None):
@ -194,7 +264,6 @@ class crm_case(object):
@param uid: the current users ID for security checks,
@param ids: List of case IDs
@param context: A standard dictionary for contextual values"""
return self.stage_change(cr, uid, ids, context=context, order='sequence')
@ -257,6 +326,7 @@ class crm_case(object):
@param ids: List of case Ids
@param *args: Tuple Value for additional Params
"""
cases = self.browse(cr, uid, ids)
self._history(cr, uid, cases, _('Open'))
for case in cases:
@ -264,7 +334,9 @@ class crm_case(object):
if not case.user_id:
data['user_id'] = uid
self.write(cr, uid, case.id, data)
self._action(cr, uid, cases, 'open')
self._action(cr, uid, cases, 'open')
return True
def case_close(self, cr, uid, ids, *args):
@ -483,6 +555,12 @@ class crm_case_stage(osv.osv):
_description = "Stage of case"
_rec_name = 'name'
_order = "sequence"
def _get_type_value(self, cr, user, context):
return [('lead','Lead'),('opportunity','Opportunity')]
_columns = {
'name': fields.char('Stage Name', size=64, required=True, translate=True),
@ -491,9 +569,10 @@ class crm_case_stage(osv.osv):
'on_change': fields.boolean('Change Probability Automatically', \
help="Change Probability on next and previous stages."),
'requirements': fields.text('Requirements'),
'type': fields.selection([('lead','Lead'),('opportunity','Opportunity'),('claim','Claim'), ('fundraising','Fundraising')], 'Type'),
'type': fields.selection(_get_type_value, 'Type'),
}
def _find_stage_type(self, cr, uid, context=None):
"""Finds type of stage according to object.
@param self: The object pointer

View File

@ -151,18 +151,6 @@ class crm_lead(crm_case, osv.osv):
'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
}
def _get_stage_id(self, cr, uid, context=None):
"""Finds type of stage according to object.
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param context: A standard dictionary for contextual values
"""
if context is None:
context = {}
type = context and context.get('stage_type', '')
stage_ids = self.pool.get('crm.case.stage').search(cr, uid, [('type','=',type),('sequence','>=',1)])
return stage_ids and stage_ids[0] or False
_defaults = {
'active': lambda *a: 1,
@ -173,8 +161,10 @@ class crm_lead(crm_case, osv.osv):
'section_id': crm_case._get_section,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
'stage_id': _get_stage_id,
#'stage_id': _get_stage_id,
}
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
"""This function returns value of partner email based on Partner Address
@ -198,27 +188,30 @@ class crm_lead(crm_case, osv.osv):
@param ids: List of case's Ids
@param *args: Give Tuple Value
"""
old_state = self.read(cr, uid, ids, ['state'])[0]['state']
old_stage_id = self.read(cr, uid, ids, ['stage_id'])[0]['stage_id']
leads = self.browse(cr, uid, ids)
for i in xrange(0, len(ids)):
if leads[i].state == 'draft':
value = {}
if not leads[i].stage_id :
stage_id = self._find_first_stage(cr, uid, leads[i].type, leads[i].section_id.id or False)
value.update({'stage_id' : stage_id})
value.update({'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
self.write(cr, uid, [ids[i]], value)
self.log_open( cr, uid, leads[i])
res = super(crm_lead, self).case_open(cr, uid, ids, *args)
if old_state == 'draft':
value = {}
if not old_stage_id:
stage_id = super(crm_lead, self).stage_next(cr, uid, ids, *args)
if stage_id:
value.update(self.onchange_stage_id(cr, uid, ids, stage_id, context={})['value'])
value.update({'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
self.write(cr, uid, ids, value)
for case in self.browse(cr, uid, ids):
if case.type == 'lead':
message = _("The lead '%s' has been opened.") % case.name
elif case.type == 'opportunity':
message = _("The opportunity '%s' has been opened.") % case.name
else:
message = _("The case '%s' has been opened.") % case.name
self.log(cr, uid, case.id, message)
return res
def log_open(self, cr, uid, case):
if case.type == 'lead':
message = _("The lead '%s' has been opened.") % case.name
elif case.type == 'opportunity':
message = _("The opportunity '%s' has been opened.") % case.name
else:
message = _("The case '%s' has been opened.") % case.name
self.log(cr, uid, case.id, message)
def case_close(self, cr, uid, ids, *args):
"""Overrides close for crm_case for setting close date
@ -228,7 +221,7 @@ class crm_lead(crm_case, osv.osv):
@param ids: List of case Ids
@param *args: Tuple Value for additional Params
"""
res = super(crm_lead, self).case_close(cr, uid, ids, args)
res = super(crm_lead, self).case_close(cr, uid, ids, *args)
self.write(cr, uid, ids, {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
for case in self.browse(cr, uid, ids):
if case.type == 'lead':
@ -296,14 +289,18 @@ class crm_lead(crm_case, osv.osv):
return value
def write(self, cr, uid, ids, vals, context=None):
if not context:
context = {}
if 'date_closed' in vals:
return super(crm_lead,self).write(cr, uid, ids, vals, context=context)
if 'stage_id' in vals and vals['stage_id']:
stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context)
self.history(cr, uid, ids, _('Stage'), details=stage_obj.name)
message=''
for case in self.browse(cr, uid, ids, context=context):
if case.type == 'lead':
if case.type == 'lead' or context.get('stage_type',False)=='lead':
message = _("The stage of lead '%s' has been changed to '%s'.") % (case.name, case.stage_id.name)
elif case.type == 'opportunity':
message = _("The stage of opportunity '%s' has been changed to '%s'.") % (case.name, case.stage_id.name)
@ -398,7 +395,6 @@ class crm_lead(crm_case, osv.osv):
if case.state in CRM_LEAD_PENDING_STATES:
values.update(state=crm.AVAILABLE_STATES[1][0]) #re-open
res = self.write(cr, uid, [case.id], values, context=context)
return res
def msg_send(self, cr, uid, id, *args, **argv):

View File

@ -13,6 +13,8 @@
<field name="context">{'type':'lead'}</field>
<field name="help">Create specific stages that will help your sales better organise their sales pipeline by maintaining them to their leads and sales opportunities. It will allow them to easily track how is positioned a specific lead or opportunity in the sales cycle.</field>
</record>
<menuitem action="crm_lead_stage_act" id="menu_crm_lead_stage_act" name="Stages"
groups="base.group_extended" sequence="0"

View File

@ -72,53 +72,14 @@ class crm_meeting(crm_case, osv.osv):
('draft', 'Unconfirmed'),
('cancel', 'Cancelled'),
('done', 'Done')], 'State', \
size=16, readonly=True)
size=16, readonly=True),
}
_defaults = {
'state': lambda *a: 'draft',
'active': lambda *a: 1,
'user_id': lambda self, cr, uid, ctx: uid,
}
def open_meeting(self, cr, uid, ids, context=None):
"""
Open Crm Meeting Form for Crm Meeting.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of crm meetings IDs
@param context: A standard dictionary for contextual values
@return: Dictionary value which open Crm Meeting form.
"""
data_obj = self.pool.get('ir.model.data')
value = {}
id2 = data_obj._get_id(cr, uid, 'crm', 'crm_case_form_view_meet')
id3 = data_obj._get_id(cr, uid, 'crm', 'crm_case_tree_view_meet')
id4 = data_obj._get_id(cr, uid, 'crm', 'crm_case_calendar_view_meet')
if id2:
id2 = data_obj.browse(cr, uid, id2, context=context).res_id
if id3:
id3 = data_obj.browse(cr, uid, id3, context=context).res_id
if id4:
id4 = data_obj.browse(cr, uid, id4, context=context).res_id
for id in ids:
value = {
'name': _('Meeting'),
'view_type': 'form',
'view_mode': 'form,tree',
'res_model': 'crm.meeting',
'view_id': False,
'views': [(id2, 'form'), (id3, 'tree'), (id4, 'calendar')],
'type': 'ir.actions.act_window',
'res_id': base_calendar.base_calendar_id2real_id(id),
'nodestroy': True
}
return value
def case_open(self, cr, uid, ids, *args):
"""Confirms meeting
@param self: The object pointer

View File

@ -44,62 +44,9 @@
<field name="alarm_id" string="Reminder"
widget="selection" />
<group colspan="2" col="4" >
<field name="rrule_type" string="Recurrency"
colspan="1" attrs="{'readonly':[('recurrent_uid','!=',False)]}"/>
<button string="Edit All"
help="Edit all Occurrences of recurrent Meeting"
attrs="{'invisible':[('rrule_type','in', ('none', False))]}"
name="open_meeting" icon="gtk-edit"
type="object" />
<button string="Exclude range" groups="base.group_extended"
help="Add Exception Rule"
name="%(base_calendar.action_base_calendar_set_exrule)d" icon="gtk-zoom-out" type="action"
context="{'model' : 'crm.meeting'}"
attrs="{'invisible':[('rrule_type','in', ('none', False))]}"/>
</group>
</group>
<group col="4" colspan="4" name="rrule" attrs="{'invisible': [('rrule_type','!=','custom')]}">
<separator string="Custom Recurrency Rule" colspan="8"/>
<group col="8" colspan="4">
<field name="interval" />
<field name="freq" />
<field name="count" />
<field name="end_date" />
</group>
<group col="14" colspan="4" name="Select weekdays"
attrs="{'invisible' : [('freq','!=','weekly')]}">
<field name="mo" colspan="1" />
<field name="tu" colspan="1" />
<field name="we" colspan="1" />
<field name="th" colspan="1" />
<field name="fr" colspan="1" />
<field name="sa" colspan="1" />
<field name="su" colspan="1" />
<newline />
</group>
<group col="10" colspan="4"
attrs="{'invisible' : [('freq','!=','monthly'), ('freq','!=','yearly')]}">
<group col="2" colspan="1">
<field name="select1" />
</group>
<group col="2" colspan="1"
attrs="{'invisible' : [('select1','=','day')]}">
<field name="day"
attrs="{'required' : [('select1','=','date')]}" />
</group>
<group col="3" colspan="1"
attrs="{'invisible' : [('select1','=','date')]}">
<field name="byday" string="The"
attrs="{'required' : [('select1','=','day')]}" />
<field name="week_list" nolabel="1"
attrs="{'required' : [('select1','=','day')]}" />
</group>
<group col="1" colspan="1"
attrs="{'invisible' : [('freq','!=','yearly')]}">
<field name="month_list" string="of"
colspan="1"
attrs="{'required' : [('freq','=','yearly')]}" />
</group>
<field name="recurrency"/>
<field name="edit_all" attrs="{'invisible':[('recurrency','=', False)]}"
on_change="onchange_edit_all(rrule_type,edit_all)"/>
</group>
</group>
<notebook colspan="4">
@ -133,14 +80,11 @@
<separator colspan="4"/>
<group col="8" colspan="4" groups="base.group_extended">
<field name="state" />
<button name="case_cancel" string="Cancel"
states="draft,open" type="object"
icon="gtk-cancel" />
<button name="case_close" string="Done"
states="open" type="object"
icon="gtk-jump-to" />
<button name="case_reset" string="Reset to Unconfirmed"
states="open,done,cancel" type="object"
states="open,done" type="object"
icon="gtk-convert" />
<button name="case_open" string="Confirm"
states="draft" type="object"
@ -221,6 +165,57 @@
</form>
</field>
</page>
<page string="Recurrency Option" attrs="{'invisible':[('recurrency','=',False)]}">
<group col="4" colspan="4" name="rrule">
<group col="4" colspan="4">
<field name="rrule_type" string="Recurrency period"
attrs="{'readonly':[('recurrent_uid','!=',False)]}" />
<label string="" colspan="2"/>
<separator string="End of recurrency" colspan="4"/>
<field name="count" attrs="{'readonly': [('end_date','!=',False)]}"/>
<label string=" " colspan="2" />
<newline />
<field name="end_date" attrs="{'readonly': [('count','!=',False)]}"/>
<newline />
<separator string="Repeat interval" colspan="4"/>
<field name="interval" /> <label string="" colspan="2"/>
</group>
<group col="8" colspan="4" name="Select weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}">
<separator string="Choose day where repeat the meeting" colspan="8"/>
<field name="mo" colspan="1" />
<field name="tu" colspan="1" />
<field name="we" colspan="1" />
<field name="th" colspan="1" />
<newline/>
<field name="fr" colspan="1" />
<field name="sa" colspan="1" />
<field name="su" colspan="1" />
<newline />
</group>
<group col="10" colspan="4"
attrs="{'invisible' : [('rrule_type','!=','monthly')]}">
<separator string="Choose day in the month where repeat the meeting" colspan="12"/>
<group col="2" colspan="1">
<field name="select1" />
</group>
<group col="2" colspan="1"
attrs="{'invisible' : [('select1','=','day')]}">
<field name="day"
attrs="{'required' : [('select1','=','date'), ('rrule_type','=','monthly')]}" />
</group>
<group col="3" colspan="1"
attrs="{'invisible' : [('select1','=','date'), ('rrule_type','=','monthly')]}">
<field name="byday" string="The"
attrs="{'required' : [('select1','=','day'), ('rrule_type','=','monthly')]}" />
<field name="week_list" nolabel="1"
attrs="{'required' : [('select1','=','day'), ('rrule_type','=','monthly')]}" />
</group>
</group>
</group>
</page>
</notebook>
</form>
</field>
@ -245,11 +240,8 @@
<field name="state" groups="base.group_extended"/>
<field name="user_id" invisible="1"/>
<field name="show_as" invisible="1" string="Show time as"/>
<button name="case_cancel" string="Cancel"
states="draft,open" type="object"
icon="gtk-cancel" />
<button name="case_reset" string="Reset to Unconfirmed"
states="open,done,cancel" type="object"
states="open,done" type="object"
icon="gtk-convert" />
<button name="case_open" string="Confirm"
states="draft" type="object"
@ -304,7 +296,7 @@
<group col="12" colspan="4">
<filter icon="terp-check" name="current" string="Current"
domain="[('state','in',('draft', 'open'))]"
groups="base.group_extended"
groups="base.group_extended"
help="Current Meetings"/>
<separator orientation="vertical"/>
<field name="name" select="1" string="Subject"/>
@ -333,8 +325,8 @@
context="{'group_by':'partner_id'}" />
<separator orientation="vertical" />
<filter string="Type" help="Meeting Type"
icon="terp-stock_symbol-selection" domain="[]"
context="{'group_by':'categ_id'}" />
icon="terp-stock_symbol-selection" domain="[]"
context="{'group_by':'categ_id'}" />
<filter string="Privacy" icon="terp-locked"
domain="[]" context="{'group_by':'class'}" />
<filter string="Show time as" icon="terp-project"

View File

@ -22,7 +22,7 @@
from osv import fields, osv
from tools.translate import _
import crm
import time
AVAILABLE_STATES = [
('draft','Draft'),
@ -53,6 +53,28 @@ class crm_opportunity(osv.osv):
'stage_id': fields.many2one('crm.case.stage', 'Stage', domain="[('type','=','opportunity')]"),
}
def _case_close_generic(self, cr, uid, ids, find_stage, *args):
res = super(crm_opportunity, self).case_close(cr, uid, ids, *args)
for case in self.browse(cr, uid, ids):
#if the case is not an opportunity close won't change the stage
if not case.type == 'opportunity':
return res
value = {}
stage_id = find_stage(cr, uid, 'opportunity', case.section_id.id or False)
if stage_id:
stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, stage_id)
value.update({'stage_id': stage_id})
if stage_obj.on_change:
value.update({'probability': stage_obj.probability})
#Done un crm.case
#value.update({'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
self.write(cr, uid, ids, value)
return res
def case_close(self, cr, uid, ids, *args):
"""Overrides close for crm_case for setting probability and close date
@param self: The object pointer
@ -61,16 +83,8 @@ class crm_opportunity(osv.osv):
@param ids: List of case Ids
@param *args: Tuple Value for additional Params
"""
res = super(crm_opportunity, self).case_close(cr, uid, ids, args)
data_obj = self.pool.get('ir.model.data')
data_id = data_obj._get_id(cr, uid, 'crm', 'stage_lead5')
stage_id = data_obj.browse(cr, uid, data_id).res_id
stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, stage_id)
value = {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'), 'stage_id': stage_id}
if stage_obj.on_change:
value.update({'probability': stage_obj.probability})
self.write(cr, uid, ids, value)
res = self._case_close_generic(cr, uid, ids, self._find_won_stage, *args)
for (id, name) in self.name_get(cr, uid, ids):
opp = self.browse(cr, uid, id)
if opp.type == 'opportunity':
@ -86,16 +100,8 @@ class crm_opportunity(osv.osv):
@param ids: List of case Ids
@param *args: Tuple Value for additional Params
"""
res = super(crm_opportunity, self).case_close(cr, uid, ids, args)
data_obj = self.pool.get('ir.model.data')
data_id = data_obj._get_id(cr, uid, 'crm', 'stage_lead6')
stage_id = data_obj.browse(cr, uid, data_id).res_id
stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, stage_id)
value = {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'), 'stage_id': stage_id}
if stage_obj.on_change:
value.update({'probability': stage_obj.probability})
self.write(cr, uid, ids, value)
res = self._case_close_generic(cr, uid, ids, self._find_lost_stage, *args)
for (id, name) in self.name_get(cr, uid, ids):
opp = self.browse(cr, uid, id)
if opp.type == 'opportunity':
@ -137,7 +143,7 @@ class crm_opportunity(osv.osv):
@param *args: Give Tuple Value
"""
res = super(crm_opportunity, self).case_open(cr, uid, ids, *args)
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
return res
def onchange_stage_id(self, cr, uid, ids, stage_id, context=None):

View File

@ -11,6 +11,8 @@
<field name="context">{'type':'opportunity'}</field>
<field name="help">Create specific stages that will help your sales better organise their sales pipeline by maintaining them to their sales opportunities. It will allow them to easily track how is positioned a specific opportunity in the sales cycle.</field>
</record>
<menuitem action="crm_opportunity_stage_act" id="menu_crm_opportunity_stage_act" name="Stages"
groups="base.group_extended" sequence="0"
@ -39,14 +41,15 @@
</group>
<field name="user_id"/>
<button name="action_makeMeeting" type="object"
string="Schedule Meeting" icon="gtk-redo" />
<button string="Schedule/Log Call"
name="%(opportunity2phonecall_act)d" icon="terp-call-start" type="action" groups="base.group_extended"/>
<field name="planned_revenue"/>
<field name="probability"/>
<field name="date_deadline"/>
<button string="Schedule/Log Call"
name="%(opportunity2phonecall_act)d" icon="terp-call-start" type="action" groups="base.group_extended"/>
<button name="action_makeMeeting" type="object"
string="Schedule Meeting" icon="gtk-redo" />
<newline/>
<field name="date_action"/>
<field name="title_action"/>

View File

@ -108,6 +108,7 @@
<field name="sequence"/>
<field name="name"/>
<field name="probability"/>
<field name="type" />
</tree>
</field>
</record>
@ -118,17 +119,19 @@
<field name="name">crm.case.stage.form</field>
<field name="model">crm.case.stage</field>
<field name="type">form</field>
<field name="priority" eval="1"/>
<field name="arch" type="xml">
<form string="Stage">
<separator string="Stage Definition" colspan="4"/>
<field name="name" select="1"/>
<field name="type" invisible="1" />
<field name="section_ids" invisible="1" />
<field name="sequence"/>
<field name="probability"/>
<field name="on_change"/>
<separator string="Requirements" colspan="4"/>
<field name="requirements" nolabel="1" colspan="4"/>
<field name="section_ids" invisible="1" />
</form>
</field>
</record>
@ -141,6 +144,7 @@
<field name="view_type">form</field>
<field name="view_id" ref="crm_case_stage_tree"/>
</record>
<!-- Case Categories Form View -->
@ -375,7 +379,7 @@
<field name="inherit_id" ref="base.view_partner_form"/>
<field eval="18" name="priority"/>
<field name="arch" type="xml">
<field name="parent_id" position="after">
<field name="user_id" position="after">
<field name="section_id" completion="1" widget="selection"
groups="base.group_extended"/>
</field>

File diff suppressed because it is too large Load Diff

View File

@ -64,6 +64,73 @@
<button name="case_close" string="Held" states="open,draft,pending" type="object" icon="gtk-jump-to"/>
<button name="case_pending" string="Not Held" states="open" type="object" icon="gtk-media-pause"/>
</tree>
<form string="Phone Call">
<group colspan="6" col="7">
<field name="name" required="1"/>
<field name="partner_phone" attrs="{'required': [('categ_id.name', '=', 'Outbound')]}"/>
<field name="duration" widget="float_time" required="1"/>
<button string="Schedule a Meeting" name="action_make_meeting" icon="gtk-redo" type="object"/>
<field name="date" required="1"/>
<field name="user_id"/>
<field name="section_id" colspan="1" widget="selection" />
<button string="aaaaaaaaaConvert to Opportunity"
name="%(phonecall2opportunity_act)d"
icon="gtk-index" type="action"
attrs="{'invisible':[('opportunity_id','!=',False)]}" />
<label colspan="6" string=""/>
<button string="Schedule Other Call"
icon="terp-call-start"
name="%(phonecall_to_phonecall_act)d"
type="action" />
</group>
<group col="3" colspan="2">
<separator colspan="3" string="Contacts" />
<field name="partner_id"
on_change="onchange_partner_id(partner_id, email_from)" />
<button string="Create a Partner"
icon="terp-partner"
name="%(action_crm_phonecall2partner)d"
type="action"
attrs="{'invisible':[('partner_id','!=',False)]}" />
<newline/>
<field name="partner_address_id"
on_change="onchange_partner_address_id(partner_address_id, email_from)" />
<newline/>
<field name="partner_mobile" />
</group>
<group col="2" colspan="2">
<separator colspan="2" string="Categorization" />
<field name="categ_id" widget="selection"
domain="[('object_id.model', '=', 'crm.phonecall')]"
string="Type" />
<field name="priority"/>
<field name="opportunity_id"/>
</group>
<separator string="Description" colspan="4" />
<field name="description" nolabel="1" colspan="4" />
<separator colspan="4" />
<group col="8" colspan="4">
<field name="state" select="1" />
<button name="case_cancel" string="Cancel"
states="draft,open,pending" type="object"
icon="gtk-cancel" />
<button name="case_open" string="Open"
states="draft,pending" type="object"
icon="gtk-go-forward" />
<button name="case_pending" string="Not Held"
states="open" type="object" icon="gtk-media-pause" />
<button name="case_close" string="Held"
states="open,draft,pending" type="object"
icon="gtk-jump-to" />
<button name="case_reset" string="Reset to Draft"
states="done,cancel" type="object"
icon="gtk-convert" />
</group>
</form>
</field>
</page>
</field>

View File

@ -42,7 +42,7 @@
and I set the fields so that the meeting will occur weekly on Monday and Friday 10 times
-
!python {model: crm.meeting}: |
self.write(cr, uid, [ref("crm_meeting_regardingpresentation0")], {'fr': 1, 'mo': 1, 'th': 1, 'tu': 1, 'we':1, 'count':10, 'interval': 1, 'freq': 'weekly', 'rrule_type': 'custom'})
self.write(cr, uid, [ref("crm_meeting_regardingpresentation0")], {'fr': 1, 'mo': 1, 'th': 1, 'tu': 1, 'we':1, 'count':10, 'interval': 1, 'rrule_type': 'weekly'})
- |
I can see from the calendar view that the meeting is scheduled on Monday and Friday
@ -62,7 +62,7 @@
Now If I want to edit meetings information for all occurrence I click on "Edit All" button.
-
!python {model: crm.meeting}: |
self.open_meeting(cr, uid, [ref('crm_meeting_regardingpresentation0')])
self.write(cr, uid, [ref('crm_meeting_regardingpresentation0')], {'edit_all':'True'},context)
- |
I can see that new meeting form is opened with same value
I change some data for meeting and save it

View File

@ -89,7 +89,7 @@ class crm_lead2opportunity(osv.osv_memory):
self.pool.get('mailgate.message').write(cr, uid, msg_ids, {
'partner_id': lead.partner_id.id
}, context=context)
self.log(cr, uid, lead.id,
leads.log(cr, uid, lead.id,
_("Lead '%s' has been converted to an opportunity.") % lead.name)
return {

View File

@ -96,7 +96,7 @@ class crm_claim(crm.crm_case, osv.osv):
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
'stage_id': _get_stage_id,
#'stage_id': _get_stage_id,
}
def onchange_partner_id(self, cr, uid, ids, part, email=False):
@ -132,7 +132,38 @@ class crm_claim(crm.crm_case, osv.osv):
return {'value': {'email_from': False}}
address = self.pool.get('res.partner.address').browse(cr, uid, add)
return {'value': {'email_from': address.email, 'partner_phone': address.phone, 'partner_mobile': address.mobile}}
def case_open(self, cr, uid, ids, *args):
"""
Opens Claim
"""
res = super(crm_claim, self).case_open(cr, uid, ids, *args)
claims = self.browse(cr, uid, ids)
for i in xrange(0, len(ids)):
if not claims[i].stage_id :
stage_id = self._find_first_stage(cr, uid, 'claim', claims[i].section_id.id or False)
self.write(cr, uid, [ids[i]], {'stage_id' : stage_id})
return res
crm_claim()
class crm_stage_claim(osv.osv):
def _get_type_value(self, cr, user, context):
list = super(crm_stage_claim, self)._get_type_value(cr, user, context)
list.append(('claim','Claim'))
return list
_inherit = "crm.case.stage"
_columns = {
'type': fields.selection(_get_type_value, 'Type'),
}
crm_stage_claim()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -33,7 +33,7 @@
<field name="context">{'type':'claim'}</field>
<field name="help">You can create claim stages to categorize the status of every claim entered in the system. The stages define all the steps required for the resolution of a claim.</field>
</record>
<menuitem action="crm_claim_stage_act" name="Stages"
id="menu_crm_claim_stage_act" parent="menu_config_claim" />
@ -70,18 +70,26 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Claims">
<group colspan="4" col="6">
<group>
<field name="name" />
<field name="date"/>
<field name="date_deadline"/>
<newline/>
</group>
<group colspan="4" col="6">
<field name="user_id"/>
<field name="priority"/>
<field name="section_id" widget="selection" />
<group colspan="2" col="4">
<field name="stage_id" domain="[('type','=','claim')]"/>
<field name="stage_id" domain="[('type','=','claim'),('section_ids', '=', section_id)]"/>
<button name="stage_previous" string="" type="object" icon="gtk-go-back" />
<button icon="gtk-go-forward" string="" name="stage_next" type="object"/>
</group>
<newline />
<field name="priority"/>
<field name="date_deadline"/>
</group>
<group colspan="4" col="4">
<notebook>

View File

@ -94,3 +94,19 @@ class crm_fundraising(crm.crm_case, osv.osv):
}
crm_fundraising()
class crm_stage_fundraising(osv.osv):
def _get_type_value(self, cr, user, context):
list = super(crm_stage_fundraising, self)._get_type_value(cr, user, context)
list.append(('fundraising','Fundraising'))
return list
_inherit = "crm.case.stage"
_columns = {
'type': fields.selection(_get_type_value, 'Type'),
}
crm_stage_fundraising()

View File

@ -33,6 +33,8 @@
<field name="context">{'type':'fundraising'}</field>
<field name="help">Create and manage fund raising activity categories you want to be maintained in the system.</field>
</record>
<menuitem action="crm_fundraising_stage_act"
groups="base.group_extended" name="Stages"

View File

@ -18,7 +18,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import netsvc
from osv import fields,osv

View File

@ -165,11 +165,9 @@ class contentIndex(object):
os.write(fd, content)
os.close(fd)
fp = Popen(['file','-b','--mime',fname], shell=False, stdout=PIPE).stdout
try:
result = fp.read()
finally:
fp.close()
pop = Popen(['file','-b','--mime',fname], shell=False, stdout=PIPE)
(result, _) = pop.communicate()
mime2 = result.split(';')[0]
self.__logger.debug('File gave us: %s', mime2)
# Note that the temporary file still exists now.

View File

@ -82,7 +82,6 @@
<group colspan="4" col="4" attrs="{'invisible': [('type','!=','ressource')]}">
<field name="ressource_type_id" on_change="onchange_content_id(ressource_type_id)"
attrs="{'required': [('type','=','ressource')] }"/>
<field name="resource_find_all" groups="base.group_extended" />
<newline/>
<field name="resource_field" domain="[('model_id','=',ressource_type_id), ('ttype', 'in', ('char', 'selection', 'date', 'datetime'))]"/>
<field name="ressource_tree"/>
@ -93,6 +92,9 @@
<field name="ressource_parent_type_id"/>
<field name="ressource_id" select="2" readonly="1"/>
</group>
<group colspan="4" col="2" attrs="{'invisible': [('type','!=','ressource'),('resource_parent_type_id','=',False)]}">
<field name="resource_find_all" groups="base.group_extended" />
</group>
</page>
<page string="Generated Files" groups="base.group_extended">

View File

@ -837,7 +837,9 @@ class node_res_dir(node_class):
where.append(('id','=',self.resm_id))
if name:
where.append((self.namefield,'=',name))
# The =like character will match underscores against any characters
# including the special ones that couldn't exist in a FTP/DAV request
where.append((self.namefield,'=like',name.replace('\\','\\\\')))
is_allowed = self.check_perms(1)
else:
is_allowed = self.check_perms(5)
@ -858,12 +860,22 @@ class node_res_dir(node_class):
for bo in obj.browse(cr, uid, resids, context=ctx):
if not bo:
continue
name = getattr(bo, self.namefield)
if not name:
res_name = getattr(bo, self.namefield)
if not res_name:
continue
# Yes! we can't do better but skip nameless records.
# Escape the name for characters not supported in filenames
res_name = res_name.replace('/','_') # any other weird char?
if name and (res_name != name):
# we have matched _ to any character, but we only meant to match
# the special ones.
# Eg. 'a_c' will find 'abc', 'a/c', 'a_c', may only
# return 'a/c' and 'a_c'
continue
res.append(self.res_obj_class(name, self.dir_id, self, self.context, self.res_model, bo))
res.append(self.res_obj_class(res_name, self.dir_id, self, self.context, self.res_model, bo))
return res
def _get_ttag(self,cr):
@ -896,7 +908,10 @@ class node_res_obj(node_class):
self.domain = parent.domain
self.displayname = path
self.dctx_dict = parent.dctx_dict
self.res_find_all = parent.res_find_all
if isinstance(parent, node_res_dir):
self.res_find_all = parent.res_find_all
else:
self.res_find_all = False
if res_bo:
self.res_id = res_bo.id
dc2 = self.context.context.copy()
@ -1016,8 +1031,10 @@ class node_res_obj(node_class):
# Directory Structure display in tree structure
if self.res_id and directory.ressource_tree:
where1 = []
if name:
where1.append(('name','=like',name.replace('\\','\\\\')))
if obj._parent_name in obj.fields_get(cr, uid):
where1 = where + [(obj._parent_name, '=', self.res_id)]
where1.append((obj._parent_name, '=', self.res_id))
namefield = directory.resource_field.name or 'name'
resids = obj.search(cr, uid, where1, context=ctx)
for bo in obj.browse(cr, uid, resids, context=ctx):
@ -1026,27 +1043,37 @@ class node_res_obj(node_class):
res_name = getattr(bo, namefield)
if not res_name:
continue
res_name = res_name.replace('/', '_')
if name and (res_name != name):
continue
# TODO Revise
klass = directory.get_node_class(directory, dynamic=True, context=ctx)
res.append(klass(res_name, dir_id=self.dir_id, parent=self, context=self.context, res_model=self.res_model, res_bo=bo))
rnode = klass(res_name, dir_id=self.dir_id, parent=self, context=self.context,
res_model=self.res_model, res_bo=bo)
rnode.res_find_all = self.res_find_all
res.append(rnode)
where2 = where + [('parent_id','=',self.dir_id) ]
ids = dirobj.search(cr, uid, where2, context=ctx)
bo = obj.browse(cr, uid, self.res_id, context=ctx)
for dirr in dirobj.browse(cr, uid, ids, context=ctx):
if name and (name != dirr.name):
continue
if dirr.type == 'directory':
klass = dirr.get_node_class(dirr, dynamic=True, context=ctx)
res.append(klass(dirr.name, dirr.id, self, self.context, self.res_model, res_bo = None, res_id = self.res_id))
res.append(klass(dirr.name, dirr.id, self, self.context, self.res_model, res_bo = bo, res_id = self.res_id))
elif dirr.type == 'ressource':
# child resources can be controlled by properly set dctx
klass = dirr.get_node_class(dirr, context=ctx)
res.append(klass(dirr.name,self,self.context, dirr, {'active_id': self.res_id}))
res.append(klass(dirr.name,self,self.context, dirr, {'active_id': self.res_id})) # bo?
fil_obj = dirobj.pool.get('ir.attachment')
if self.res_find_all:
where2 = where
where3 = where2 + [('res_model', '=', self.res_model), ('res_id','=',self.res_id)]
# print "where clause for dir_obj", where2
where3 = where2 + [('res_model', '=', self.res_model), ('res_id','=',self.res_id)]
# print "where clause for dir_obj", where3
ids = fil_obj.search(cr, uid, where3, context=ctx)
if ids:
for fil in fil_obj.browse(cr, uid, ids, context=ctx):
@ -1057,17 +1084,19 @@ class node_res_obj(node_class):
# Get Child Ressource Directories
if directory.ressource_type_id and directory.ressource_type_id.id:
where4 = where + [('ressource_parent_type_id','=',directory.ressource_type_id.id)]
where5 = where4 + [('ressource_id','=',0)]
where5 = where4 + ['|', ('ressource_id','=',0), ('ressource_id','=',self.res_id)]
dirids = dirobj.search(cr,uid, where5)
where5 = where4 + [('ressource_id','=',self.res_id)]
dirids = dirids + dirobj.search(cr,uid, where5)
for dirr in dirobj.browse(cr, uid, dirids, context=ctx):
if dirr.type == 'directory' and not dirr.parent_id:
klass = dirr.get_node_class(dirr, dynamic=True, context=ctx)
res.append(klass(dirr.name, dirr.id, self, self.context, self.res_model, res_bo = None, res_id = self.res_id))
rnode = klass(dirr.name, dirr.id, self, self.context, self.res_model, res_bo = bo, res_id = self.res_id)
rnode.res_find_all = dirr.resource_find_all
res.append(rnode)
if dirr.type == 'ressource':
klass = dirr.get_node_class(dirr, context=ctx)
res.append(klass(dirr.name, self, self.context, dirr, {'active_id': self.res_id}))
rnode = klass(dirr.name, self, self.context, dirr, {'active_id': self.res_id})
rnode.res_find_all = dirr.resource_find_all
res.append(rnode)
return res
def create_child_collection(self, cr, objname):
@ -1092,7 +1121,8 @@ class node_res_obj(node_class):
'name': objname,
'ressource_parent_type_id': obj and obj.ressource_type_id.id or False,
'ressource_id': object2 and object2.id or False,
'parent_id' : False
'parent_id' : False,
'resource_find_all': False,
}
if (obj and (obj.type in ('directory'))) or not object2:
val['parent_id'] = obj and obj.id or False

View File

@ -3,9 +3,13 @@
import sys
import os
import glob
import time
import logging
from optparse import OptionParser
logging.basicConfig(level=logging.DEBUG)
parser = OptionParser()
parser.add_option("-q", "--quiet",
action="store_false", dest="verbose", default=True,
@ -15,6 +19,10 @@ parser.add_option("-C", "--content",
action="store_true", dest="docontent", default=False,
help="Disect content, rather than the file.")
parser.add_option("--delay",
action="store_true", dest="delay", default=False,
help="delay after the operation, to inspect child processes")
(options, args) = parser.parse_args()
import content_index, std_index
@ -34,9 +42,12 @@ for fname in args:
if options.verbose:
for line in res[:5]:
print line
if options.delay:
time.sleep(30)
except Exception,e:
import traceback
tb_s = reduce(lambda x, y: x+y, traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback))
except KeyboardInterrupt:
print "Keyboard interrupt"
#eof

View File

@ -35,6 +35,9 @@ import urllib
from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
from cache import memoize
from tools import misc
from webdav import mk_lock_response
try:
from tools.dict_tools import dict_merge2
except ImportError:
@ -209,18 +212,20 @@ class openerp_dav_handler(dav_interface):
self.parent.log_error("Cannot %s: %s", opname, err.strerror)
self.parent.log_message("Exc: %s",traceback.format_exc())
raise default_exc(err.strerror)
except Exception,e:
except Exception, e:
import traceback
if cr: cr.close()
self.parent.log_error("Cannot %s: %s", opname, str(e))
self.parent.log_message("Exc: %s",traceback.format_exc())
raise default_exc("Operation failed")
#def _get_dav_lockdiscovery(self, uri):
# raise DAV_NotFound
def _get_dav_lockdiscovery(self, uri):
""" We raise that so that the node API is used """
raise DAV_NotFound
#def A_get_dav_supportedlock(self, uri):
# raise DAV_NotFound
def _get_dav_supportedlock(self, uri):
""" We raise that so that the node API is used """
raise DAV_NotFound
def match_prop(self, uri, match, ns, propname):
if self.M_NS.has_key(ns):
@ -346,7 +351,7 @@ class openerp_dav_handler(dav_interface):
""" Return the base URI of this request, or even join it with the
ajoin path elements
"""
return self.baseuri+ '/'.join(ajoin)
return self.parent.get_baseuri(self) + '/'.join(ajoin)
@memoize(4)
def db_list(self):
@ -911,6 +916,91 @@ class openerp_dav_handler(dav_interface):
cr.close()
return result
def unlock(self, uri, token):
""" Unlock a resource from that token
@return True if unlocked, False if no lock existed, Exceptions
"""
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
if cr: cr.close()
raise DAV_Error, 409
node = self.uri2object(cr, uid, pool, uri2)
try:
node_fn = node.dav_unlock
except AttributeError:
# perhaps the node doesn't support locks
cr.close()
raise DAV_Error(400, 'No locks for this resource')
res = self._try_function(node_fn, (cr, token), "unlock %s" % uri, cr=cr)
cr.commit()
cr.close()
return res
def lock(self, uri, lock_data):
""" Lock (may create) resource.
Data is a dict, may contain:
depth, token, refresh, lockscope, locktype, owner
"""
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
created = False
if not dbname:
if cr: cr.close()
raise DAV_Error, 409
try:
node = self.uri2object(cr, uid, pool, uri2[:])
except Exception:
node = False
objname = misc.ustr(uri2[-1])
if not node:
dir_node = self.uri2object(cr, uid, pool, uri2[:-1])
if not dir_node:
cr.close()
raise DAV_NotFound('Parent folder not found')
# We create a new node (file) but with empty data=None,
# as in RFC4918 p. 9.10.4
node = self._try_function(dir_node.create_child, (cr, objname, None),
"create %s" % objname, cr=cr)
if not node:
cr.commit()
cr.close()
raise DAV_Error(400, "Failed to create resource")
created = True
try:
node_fn = node.dav_lock
except AttributeError:
# perhaps the node doesn't support locks
cr.close()
raise DAV_Error(400, 'No locks for this resource')
# Obtain the lock on the node
lres, pid, token = self._try_function(node_fn, (cr, lock_data), "lock %s" % objname, cr=cr)
if not lres:
cr.commit()
cr.close()
raise DAV_Error(423, "Resource already locked")
assert isinstance(lres, list), 'lres: %s' % repr(lres)
try:
data = mk_lock_response(self, uri, lres)
cr.commit()
except Exception:
cr.close()
raise
cr.close()
return created, data, token
@memoize(CACHE_SIZE)
def is_collection(self, uri):
""" test if the given uri is a collection """

View File

@ -80,6 +80,10 @@ class dav_dir_property(osv.osv):
_name = 'document.webdav.dir.property'
_columns = {
'create_date': fields.datetime('Date Created', readonly=True),
'create_uid': fields.many2one('res.users', 'Creator', readonly=True),
'write_date': fields.datetime('Date Modified', readonly=True),
'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True),
'dir_id': fields.many2one('document.directory', 'Directory', required=False, select=1),
'namespace': fields.char('Namespace', size=127, required=True),
'name': fields.char('Name', size=64, required=True),
@ -93,4 +97,34 @@ class dav_dir_property(osv.osv):
dav_dir_property()
class dav_file_property(osv.osv):
""" Arbitrary WebDAV properties, attached to ir.attachments.
A special case is the locks that can be applied on file nodes.
There _can_ be properties without a file (RFC?), which means that they
globally apply to all the attachments of the present database.
TODO access permissions, per property.
"""
_name = 'document.webdav.file.property'
_columns = {
'create_date': fields.datetime('Date Created', readonly=True),
'create_uid': fields.many2one('res.users', 'Creator', readonly=True),
'write_date': fields.datetime('Date Modified', readonly=True),
'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True),
'file_id': fields.many2one('ir.attachment', 'Document', required=False, select=1),
'namespace': fields.char('Namespace', size=127, required=True),
'name': fields.char('Name', size=64, required=True),
'value': fields.text('Value'),
'do_subst': fields.boolean('Substitute', required=True),
}
_defaults = {
'do_subst': False,
}
dav_file_property()
#eof

View File

@ -22,14 +22,14 @@
from document import nodes
from tools.safe_eval import safe_eval as eval
import time
import urllib
import uuid
try:
from tools.dict_tools import dict_filter
except ImportError:
from document.dict_tools import dict_filter
import urllib
class node_acl_mixin(object):
def _get_dav_owner(self, cr):
return self.uuser
@ -116,6 +116,153 @@ class node_acl_mixin(object):
return val
return None
def _dav_lock_hlpr(self, cr, lock_data, par_class, prop_model,
prop_ref_field, res_id):
""" Helper, which uses the dav properties table for placing locks
@param lock_data a dictionary of input to this function.
@return list of tuples, DAV:activelock _contents_ structure.
See webdav.py:class Prop2Xml() for semantics
Note: although the DAV response shall be an <activelock/>, this
function will only return the elements inside the activelock,
because the calling function needs to append the <lockroot/> in
it. See webdav.py:mk_lock_response()
In order to reuse code, this function can be called with
lock_data['unlock_mode']=True, in order to unlock.
@return bool in unlock mode, (davstruct, prop_id, token) in lock/refresh,
or (False, prop_id, token) if already locked,
or (False, False, False) if lock not found to refresh
"""
assert prop_model
assert res_id
assert isinstance(lock_data, dict), '%r' % lock_data
propobj = self.context._dirobj.pool.get(prop_model)
uid = self.context.uid
ctx = self.context.context.copy()
ctx.update(self.dctx)
ctx.update({'uid': uid, 'dbname': self.context.dbname })
ctx['node_classname'] = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
dict_filter(self.context.extra_ctx, ['username', 'groupname', 'webdav_path'], ctx)
sdomain = [(prop_ref_field, '=', res_id), ('namespace', '=', 'DAV:'),
('name','=', 'lockdiscovery')]
props_to_delete = []
lock_found = False
lock_val = None
tmout2 = int(lock_data.get('timeout', 3*3600))
prop_ids = propobj.search(cr, uid, sdomain, context=ctx)
if prop_ids:
for pbro in propobj.browse(cr, uid, prop_ids, context=ctx):
val = pbro.value
if pbro.do_subst:
if val.startswith("('") and val.endswith(")"):
glbls = { 'urlquote': urllib.quote, }
val = eval(val, glbls, ctx)
else:
# all locks should be at "subst" format
continue
if not (val and isinstance(val, tuple)
and val[0:2] == ( 'activelock','DAV:')):
# print "Value is not activelock:", val
continue
old_token = False
old_owner = False
try:
# discover the timeout. If anything goes wrong, delete
# the lock (cleanup)
tmout = False
for parm in val[2]:
if parm[1] != 'DAV:':
continue
if parm[0] == 'timeout':
if isinstance(parm[2], basestring) \
and parm[2].startswith('Second-'):
tmout = int(parm[2][7:])
elif parm[0] == 'locktoken':
if isinstance(parm[2], basestring):
old_token = parm[2]
elif isinstance(parm[2], tuple) and \
parm[2][0:2] == ('href','DAV:'):
old_token = parm[2][2]
else:
# print "Mangled token in DAV property: %r" % parm[2]
props_to_delete.append(pbro.id)
continue
elif parm[0] == 'owner':
old_owner = parm[2] # not used yet
if tmout:
mdate = pbro.write_date or pbro.create_date
mdate = time.mktime(time.strptime(mdate,'%Y-%m-%d %H:%M:%S'))
if mdate + tmout < time.time():
props_to_delete.append(pbro.id)
continue
else:
props_to_delete.append(pbro.id)
continue
except ValueError:
props_to_delete.append(pbro.id)
continue
# A valid lock is found here
if lock_data.get('refresh', False):
if old_token != lock_data.get('token'):
continue
# refresh mode. Just touch anything and the ORM will update
# the write uid+date, won't it?
# Note: we don't update the owner, because incoming refresh
# wouldn't have a body, anyway.
propobj.write(cr, uid, [pbro.id,], { 'name': 'lockdiscovery'})
elif lock_data.get('unlock_mode', False):
if old_token != lock_data.get('token'):
continue
props_to_delete.append(pbro.id)
lock_found = pbro.id
lock_val = val
if tmout2 > 3*3600: # 3 hours maximum
tmout2 = 3*3600
elif tmout2 < 300:
# 5 minutes minimum, but an unlock request can always
# break it at any time. Ensures no negative values, either.
tmout2 = 300
if props_to_delete:
# explicitly delete, as admin, any of the ids we have identified.
propobj.unlink(cr, 1, props_to_delete)
if lock_data.get('unlock_mode', False):
return lock_found and True
elif (not lock_found) and not (lock_data.get('refresh', False)):
# Create a new lock, attach and return it.
new_token = uuid.uuid4().urn
lock_val = ('activelock', 'DAV:',
[ ('locktype', 'DAV:', (lock_data.get('locktype',False) or 'write','DAV:')),
('lockscope', 'DAV:', (lock_data.get('lockscope',False) or 'exclusive','DAV:')),
# ? ('depth', 'DAV:', lock_data.get('depth','0') ),
('timeout','DAV:', 'Second-%d' % tmout2),
('locktoken', 'DAV:', ('href', 'DAV:', new_token)),
# ('lockroot', 'DAV: ..., we don't store that, appended by caller
])
new_owner = lock_data.get('lockowner',False) or ctx.get('username', False)
if new_owner:
lock_val[2].append( ('owner', 'DAV:', new_owner) )
prop_id = propobj.create(cr, uid, { prop_ref_field: res_id,
'namespace': 'DAV:', 'name': 'lockdiscovery',
'do_subst': True, 'value': repr(lock_val) })
return (lock_val[2], prop_id, new_token )
elif not lock_found: # and refresh
return (False, False, False)
elif lock_found and not lock_data.get('refresh', False):
# already locked
return (False, lock_found, old_token)
else:
return (lock_val[2], lock_found, old_token )
class node_dir(node_acl_mixin, nodes.node_dir):
""" override node_dir and add DAV functionality
"""
@ -141,7 +288,8 @@ class node_dir(node_acl_mixin, nodes.node_dir):
class node_file(node_acl_mixin, nodes.node_file):
DAV_PROPS = { "DAV:": ('owner', 'group',
'supported-privilege-set',
'current-user-privilege-set'),
'current-user-privilege-set',
),
}
DAV_M_NS = { "DAV:" : '_get_dav',}
http_options = { 'DAV': ['access-control', ] }
@ -152,10 +300,45 @@ class node_file(node_acl_mixin, nodes.node_file):
def get_dav_props(self, cr):
return self._get_dav_props_hlpr(cr, nodes.node_dir,
None, 'file_id', self.file_id)
#'document.webdav.dir.property', 'dir_id', self.dir_id)
'document.webdav.file.property', 'file_id', self.file_id)
#def get_dav_eprop(self, cr, ns, prop):
def dav_lock(self, cr, lock_data):
""" Locks or unlocks the node, using DAV semantics.
Unlocking will be done when lock_data['unlock_mode'] == True
See _dav_lock_hlpr() for calling details.
It is fundamentally OK to use this function from non-DAV endpoints,
but they will all have to emulate the tuple-in-list structure of
the DAV lock data. RFC if this translation should be done inside
the _dav_lock_hlpr (to ease other protocols).
"""
return self._dav_lock_hlpr(cr, lock_data, nodes.node_file,
'document.webdav.file.property', 'file_id', self.file_id)
def dav_unlock(self, cr, token):
"""Releases the token lock held for the node
This is a utility complement of dav_lock()
"""
lock_data = { 'token': token, 'unlock_mode': True }
return self._dav_lock_hlpr(cr, lock_data, nodes.node_file,
'document.webdav.file.property', 'file_id', self.file_id)
def get_dav_eprop(self, cr, ns, prop):
if ns == 'DAV:' and prop == 'supportedlock':
return [ ('lockentry', 'DAV:',
[ ('lockscope','DAV:', ('shared', 'DAV:')),
('locktype','DAV:', ('write', 'DAV:')),
]),
('lockentry', 'DAV:',
[ ('lockscope','DAV:', ('exclusive', 'DAV:')),
('locktype','DAV:', ('write', 'DAV:')),
] )
]
return self._get_dav_eprop_hlpr(cr, ns, prop, nodes.node_file,
'document.webdav.file.property', 'file_id', self.file_id)
class node_database(nodes.node_database):
def get_dav_resourcetype(self, cr):

View File

@ -2,3 +2,4 @@
"access_webdav_dir_property_all","webdav.dir.property all","model_document_webdav_dir_property",,1,0,0,0
"access_webdav_dir_property_group_doc_manager","webdav.dir.property document manager","model_document_webdav_dir_property","base.group_system",1,1,1,1
"access_webdav_dir_property_group_system","webdav.dir.property group system","model_document_webdav_dir_property","base.group_system",1,1,1,1
"access_webdav_file_property_all","webdav.file.property all","model_document_webdav_file_property",,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_webdav_dir_property_all webdav.dir.property all model_document_webdav_dir_property 1 0 0 0
3 access_webdav_dir_property_group_doc_manager webdav.dir.property document manager model_document_webdav_dir_property base.group_system 1 1 1 1
4 access_webdav_dir_property_group_system webdav.dir.property group system model_document_webdav_dir_property base.group_system 1 1 1 1
5 access_webdav_file_property_all webdav.file.property all model_document_webdav_file_property 1 1 1 1

View File

@ -1,12 +1,17 @@
-
In order to test the document_ftp functionality
In order to test the document_webdav functionality
-
I open the HTTP port and perform an OPTIONS request to the server
-
!python {model: ir.attachment}: |
from document_webdav import test_davclient as te
reload(te) # reload..
dc = te.DAVClient()
dc = te.DAVClient(timeout=2.0)
# have a small timeout, enough for any heavily-loaded test server to
# respond, but small so that this test won't block further loading.
# Don't catch the exception, so that the whole YAML test will abort
# if the WebDAV service is not available (eg. during an upgrade from
# command line).
dc.gd_options()
dc.get_creds(self, cr, uid)
dc.gd_options(path=cr.dbname, expect={'DAV': ['1',]})

View File

@ -324,7 +324,7 @@ class DAVClient(object):
"""An instance of a WebDAV client, connected to the OpenERP server
"""
def __init__(self, user=None, passwd=None, dbg=0, use_ssl=False, useragent=False):
def __init__(self, user=None, passwd=None, dbg=0, use_ssl=False, useragent=False, timeout=None):
if use_ssl:
self.host = config.get_misc('httpsd', 'interface', False)
self.port = config.get_misc('httpsd', 'port', 8071)
@ -346,6 +346,7 @@ class DAVClient(object):
self.user = user
self.passwd = passwd
self.dbg = dbg
self.timeout = timeout or 5.0 # seconds, tests need to respond pretty fast!
self.hdrs = {}
if useragent:
self.set_useragent(useragent)
@ -386,7 +387,7 @@ class DAVClient(object):
dbg = self.dbg
hdrs.update(self.hdrs)
log.debug("Getting %s http://%s:%d/%s", method, self.host, self.port, path)
conn = httplib.HTTPConnection(self.host, port=self.port)
conn = httplib.HTTPConnection(self.host, port=self.port, timeout=self.timeout)
conn.set_debuglevel(dbg)
if not path:
path = "/index.html"

View File

@ -268,6 +268,39 @@ def mk_propname_response(self,uri,propnames,doc):
PROPFIND.mk_prop_response = mk_prop_response
PROPFIND.mk_propname_response = mk_propname_response
def mk_lock_response(self, uri, props):
""" Prepare the data response to a DAV LOCK command
This function is here, merely to be in the same file as the
ones above, that have similar code.
"""
doc = domimpl.createDocument('DAV:', "D:prop", None)
ms = doc.documentElement
ms.setAttribute("xmlns:D", "DAV:")
# ms.tagName = 'D:multistatus'
namespaces = []
nsnum = 0
propgen = Prop2xml(doc, namespaces, nsnum)
# write href information
uparts=urlparse.urlparse(uri)
fileloc=uparts[2]
if isinstance(fileloc, unicode):
fileloc = fileloc.encode('utf-8')
davpath = self.parent.get_davpath()
if uparts[0] and uparts[1]:
hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
else:
# When the request has been relative, we don't have enough data to
# reply with absolute url here.
hurl = '%s%s' % (davpath, urllib.quote(fileloc))
props.append( ('lockroot', 'DAV:', ('href', 'DAV:', (hurl))))
pld = doc.createElement('D:lockdiscovery')
ms.appendChild(pld)
propgen._prop_child(pld, 'DAV:', 'activelock', props)
return doc.toxml(encoding="utf-8")
super_create_prop = REPORT.create_prop
def create_prop(self):

View File

@ -1,8 +1,14 @@
# -*- encoding: utf-8 -*-
#
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
# Copyright P. Christeas <p_christ@hol.gr> 2008-2010
#
# Disclaimer: Many of the functions below borrow code from the
# python-webdav library (http://code.google.com/p/pywebdav/ ),
# which they import and override to suit OpenERP functionality.
# python-webdav was written by: Simon Pamies <s.pamies@banality.de>
# Christian Scholz <mrtopf@webdav.de>
# Vince Spicer <vince@vince.ca>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
@ -38,7 +44,9 @@ import urllib
import re
from string import atoi
from DAV.errors import *
from DAV.utils import IfParser, TagList
# from DAV.constants import DAV_VERSION_1, DAV_VERSION_2
from xml.dom import minidom
khtml_re = re.compile(r' KHTML/([0-9\.]+) ')
@ -103,6 +111,7 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler):
self.headers['Destination'] = up.path[len(self.davpath):]
else:
raise DAV_Forbidden("Not allowed to copy/move outside webdav path")
# TODO: locks
DAVRequestHandler.copymove(self, CLASS)
def get_davpath(self):
@ -262,6 +271,139 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler):
except DAV_Error, (ec, dd):
return self.send_status(ec)
def do_UNLOCK(self):
""" Unlocks given resource """
dc = self.IFACE_CLASS
self.log_message('UNLOCKing resource %s' % self.headers)
uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
uri = urllib.unquote(uri)
token = self.headers.get('Lock-Token', False)
if token:
token = token.strip()
if token[0] == '<' and token[-1] == '>':
token = token[1:-1]
else:
token = False
if not token:
return self.send_status(400, 'Bad lock token')
try:
res = dc.unlock(uri, token)
except DAV_Error, (ec, dd):
return self.send_status(ec, dd)
if res == True:
self.send_body(None, '204', 'OK', 'Resource unlocked.')
else:
# We just differentiate the description, for debugging purposes
self.send_body(None, '204', 'OK', 'Resource not locked.')
def do_LOCK(self):
""" Attempt to place a lock on the given resource.
"""
dc = self.IFACE_CLASS
lock_data = {}
self.log_message('LOCKing resource %s' % self.headers)
body = None
if self.headers.has_key('Content-Length'):
l = self.headers['Content-Length']
body = self.rfile.read(atoi(l))
depth = self.headers.get('Depth', 'infinity')
uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
uri = urllib.unquote(uri)
self.log_message('do_LOCK: uri = %s' % uri)
ifheader = self.headers.get('If')
if ifheader:
ldif = IfParser(ifheader)
if isinstance(ldif, list):
if len(ldif) !=1 or (not isinstance(ldif[0], TagList)) \
or len(ldif[0].list) != 1:
raise DAV_Error(400, "Cannot accept multiple tokens")
ldif = ldif[0].list[0]
if ldif[0] == '<' and ldif[-1] == '>':
ldif = ldif[1:-1]
lock_data['token'] = ldif
if not body:
lock_data['refresh'] = True
else:
lock_data['refresh'] = False
lock_data.update(self._lock_unlock_parse(body))
if lock_data['refresh'] and not lock_data.get('token', False):
raise DAV_Error(400, 'Lock refresh must specify token')
lock_data['depth'] = depth
try:
created, data, lock_token = dc.lock(uri, lock_data)
except DAV_Error, (ec, dd):
return self.send_status(ec, dd)
headers = {}
if not lock_data['refresh']:
headers['Lock-Token'] = '<%s>' % lock_token
if created:
self.send_body(data, '201', 'Created', ctype='text/xml', headers=headers)
else:
self.send_body(data, '200', 'OK', ctype='text/xml', headers=headers)
def _lock_unlock_parse(self, body):
# Override the python-webdav function, with some improvements
# Unlike the py-webdav one, we also parse the owner minidom elements into
# pure pythonic struct.
doc = minidom.parseString(body)
data = {}
owners = []
for info in doc.getElementsByTagNameNS('DAV:', 'lockinfo'):
for scope in info.getElementsByTagNameNS('DAV:', 'lockscope'):
for scc in scope.childNodes:
if scc.nodeType == info.ELEMENT_NODE \
and scc.namespaceURI == 'DAV:':
data['lockscope'] = scc.localName
break
for ltype in info.getElementsByTagNameNS('DAV:', 'locktype'):
for ltc in ltype.childNodes:
if ltc.nodeType == info.ELEMENT_NODE \
and ltc.namespaceURI == 'DAV:':
data['locktype'] = ltc.localName
break
for own in info.getElementsByTagNameNS('DAV:', 'owner'):
for ono in own.childNodes:
if ono.nodeType == info.TEXT_NODE:
if ono.data:
owners.append(ono.data)
elif ono.nodeType == info.ELEMENT_NODE \
and ono.namespaceURI == 'DAV:' \
and ono.localName == 'href':
href = ''
for hno in ono.childNodes:
if hno.nodeType == info.TEXT_NODE:
href += hno.data
owners.append(('href','DAV:', href))
if len(owners) == 1:
data['lockowner'] = owners[0]
elif not owners:
pass
else:
data['lockowner'] = owners
return data
from service.http_server import reg_http_service,OpenERPAuthProvider
class DAVAuthProvider(OpenERPAuthProvider):

View File

@ -37,7 +37,7 @@
<field name="model">document.webdav.dir.property</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Search Document storage">
<search string="Search Document properties">
<field name="name" />
<field name="namespace" />
<newline/>
@ -92,7 +92,68 @@
</page>
</field>
</record>
<!-- File properties -->
<record model="ir.ui.view" id="view_file_props_form">
<field name="name">document.webdav.file.property.form</field>
<field name="model">document.webdav.file.property</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Properties">
<field name="namespace"/>
<field name="name"/>
<newline />
<field name="file_id" />
<field name="do_subst" />
<newline />
<field name="value" colspan="4" />
</form>
</field>
</record>
<record model="ir.ui.view" id="view_file_props_tree">
<field name="name">document.webdav.file.property.tree</field>
<field name="model">document.webdav.file.property</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Properties" toolbar="1">
<field name="file_id" />
<field name="namespace"/>
<field name="name"/>
</tree>
</field>
</record>
<record id="view_file_props_filter" model="ir.ui.view">
<field name="name">Search View: File DAV properties</field>
<field name="model">document.webdav.file.property</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Search Document properties">
<field name="name" />
<field name="namespace" />
<newline/>
<group expand="0" string="Group By..." groups="base.group_extended">
<filter string="Document" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'file_id'}"/>
<filter string="Namespace" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'namespace'}"/>
</group>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_file_props_form">
<field name="type">ir.actions.act_window</field>
<field name="res_model">document.webdav.file.property</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_file_props_filter"/>
</record>
<menuitem
name="DAV properties for documents"
action="action_file_props_form"
id="menu_file_props"
groups="base.group_no_one"
parent="document.menu_document_management_configuration"/>
</data>
</openerp>

View File

@ -94,7 +94,7 @@
-
!python {model: hr.sign.in.project}: |
uid = ref('test_timesheet_user1')
self.check_state(cr, uid, [ref("hr_employee_fracline1")], {"active_ids": [ref("hr_timesheet.menu_hr_timesheet_sign_in")]})
self.check_state(cr, uid, [ref("hr_employee_fracline1")], {"active_ids": [ref("hr_timesheet.action_hr_timesheet_sign_in")]})
-
I select start date and Perform start work on project.
-
@ -110,7 +110,7 @@
!python {model: hr.sign.in.project}: |
uid = ref('test_timesheet_user1')
ids = self.search(cr, uid, [('emp_id', '=', ref('hr_employee_fracline1')),('name', '=', 'Francline')])
self.check_state(cr, uid, ids, {"active_ids": [ref("hr_timesheet.menu_hr_timesheet_sign_in")]
self.check_state(cr, uid, ids, {"active_ids": [ref("hr_timesheet.action_hr_timesheet_sign_in")]
})
-

View File

@ -51,12 +51,6 @@
<field name="help">Employees can encode their time spent on the different projects. A project is an analytic account and the time spent on a project generate costs on the analytic account. This feature allows to record at the same time the attendance and the timesheet.</field>
</record>
<menuitem action="action_hr_timesheet_sign_in"
id="menu_hr_timesheet_sign_in"
groups="base.group_extended"
parent="hr_attendance.menu_hr_attendance"
sequence="5" />
<record id="view_hr_timesheet_sign_out" model="ir.ui.view">
<field name="name">hr.sign.out.project.form</field>
<field name="model">hr.sign.out.project</field>

View File

@ -63,7 +63,6 @@ class hr_timesheet_invoice_create(osv.osv_memory):
if context is None:
context = {}
result = mod_obj._get_id(cr, uid, 'account', 'view_account_invoice_filter')
res = mod_obj.read(cr, uid, result, ['res_id'], context=context)
data = self.read(cr, uid, ids, [], context=context)[0]
account_ids = {}

View File

@ -3,7 +3,17 @@
<data>
<!-- Partners inherited form -->
<record id="base.view_crm_partner_info_History" model="ir.ui.view">
<field name="name">res.partner.crm.history.inherit1</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="/form/notebook/page[@string='History']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
</field>
</record>
<record id="view_emails_partner_info_form" model="ir.ui.view">
<field name="name">res.partner.emails.info.inherit</field>
<field name="model">res.partner</field>

View File

@ -3,7 +3,17 @@
<data>
<!-- Partners inherited form -->
<record id="base.view_crm_partner_info_History" model="ir.ui.view">
<field name="name">res.partner.crm.history.inherit1</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="/form/notebook/page[@string='History']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
</field>
</record>
<record id="view_workitem_partner_info_form" model="ir.ui.view">
<field name="name">res.partner.workitem.info.inherit</field>
<field name="model">res.partner</field>

View File

@ -39,7 +39,7 @@ class outlook_installer(osv.osv_memory):
_columns = {
'name':fields.char('File name', size=34),
'doc_name':fields.char('File name', size=64),
'outlook':fields.boolean('Outlook Plug-in ', help="Allows you to select an object that youd like to add to your email and its attachments."),
'outlook':fields.boolean('Outlook Plug-in ', help="Allows you to select an object that you would like to add to your email and its attachments."),
'plugin_file':fields.binary('Outlook Plug-in', readonly=True, help="outlook plug-in file. Save as this file and install this plug-in in outlook."),
'doc_file':fields.char('Installation Manual',size="264",help="The documentation file :- how to install Outlook Plug-in.", readonly=True),
'description':fields.text('Description', readonly=True)

View File

@ -175,6 +175,12 @@ class OutlookAddin:
toolbar = bars.Item("Standard")
openerp_bar = bars.Item('Open ERP')
item = openerp_bar.Controls.Add(Type = constants.msoControlButton, Temporary = True)
item = self.toolbarButtonConfig = DispatchWithEvents(item, Configuration)
item.Caption = "Configuration"
item.TooltipText = "Click to configure OpenERP."
item.Enabled = True
item = openerp_bar.Controls.Add(Type=constants.msoControlButton, Temporary=True)
# Hook events for the item
item = self.toolbarButton = DispatchWithEvents(item, ArchiveEvent)

View File

@ -705,22 +705,18 @@ def GetSearchText(txtProcessor,*args):
b = check()
if not b:
return
search_box = txtProcessor.GetControl()
global search_text
if txtProcessor.init_done:
win32gui.SendMessage(search_box, win32con.WM_SETTEXT, 0,search_text)
return
# Get the selected mail and set the default value for search_text_control to mail.SenderEmailAddress
ex = txtProcessor.window.manager.outlook.ActiveExplorer()
assert ex.Selection.Count == 1
mail = ex.Selection.Item(1)
try:
search_text = ustr(mail.SenderEmailAddress).encode('iso-8859-1')
except Exception:
pass
win32gui.SendMessage(search_box, win32con.WM_SETTEXT, 0, search_text)
global objects_with_match
list_hwnd = win32gui.GetDlgItem(txtProcessor.window.hwnd, txtProcessor.other_ids[1])
objects_with_match = NewConn.SearchPartners()
setList(list_hwnd)
except Exception,e:
msg=getMessage(e)
win32ui.MessageBox('Document can not be loaded.\n'+str(e), "Push", flag_error)
txtProcessor.init_done=True
def SetNameColumn(listProcessor,*args):
@ -1406,14 +1402,11 @@ def OpenPartnerForm(txtProcessor,*args):
txtProcessor.init_done=True
return
try:
linktopartner = "http://"+web_server+":"+str(web_server_port)+"/openerp/form/view?model=res.partner&id="+str(vals)
import urllib
next = urllib.urlencode({'next' : '/openerp/form/view?model=res.partner&id=' +str(vals) })
weburl = 'http://'+web_server+':'+str(web_server_port)+'/'
linktopartner = weburl + '?' + next
win32gui.SendMessage(partner_link, win32con.WM_SETTEXT, 0, str(linktopartner))
# import urllib
# encode = urllib.urlencode('/openerp/form/view?model=res.partner&id='+str(vals))
# weburl = 'http://'+web_server+':'+str(web_server_port)
# linktopartner = weburl + "?next=" + encode
# win32gui.SendMessage(partner_link, win32con.WM_SETTEXT, 0, linktopartner)
except Exception,e:
win32ui.MessageBox("Error While Opening Partner.\n"+str(e),"Open Partner", flag_error)
webbrowser.open_new(linktopartner)
@ -1469,7 +1462,10 @@ def SerachOpenDocuemnt(txtProcessor,*args):
txtProcessor.init_done=True
return
try:
linktodoc = "http:\\\\" +web_server+ ":"+str(web_server_port)+"\\openerp\\form\\view?model="+vals[0][1]+"&id="+str(vals[1][1])
import urllib
next = urllib.urlencode({'next' : '/openerp/form/view?model='+vals[0][1]+'&id='+str(vals[1][1])})
weburl = 'http://'+web_server+':'+str(web_server_port)+'/'
linktodoc = weburl + '?' + next
win32gui.SendMessage(link_box, win32con.WM_SETTEXT, 0, str(linktodoc))
except Exception,e:
win32ui.MessageBox("Error While Opening Document.\n"+str(e),"Open Document", flag_error)
@ -1695,12 +1691,13 @@ dialog_map = {
(CommandButtonProcessor, "ID_SEARCH ID_SEARCH_TEXT IDC_NAME_LIST", SearchObjectsForText,()),
(GroupProcessor, "IDC_STATIC_GROUP", setCheckList, ()),
(CSComboProcessor, "ID_ATT_METHOD_DROPDOWNLIST", GetConn,()),
(TextProcessor, "ID_SEARCH_TEXT", GetSearchText, ()),
(DialogCommand, "ID_CREATE_CONTACT ID_SEARCH_TEXT", "IDD_NEW_CONTACT_DIALOG", set_search_text, ()),
(CloseButtonProcessor, "IDCANCEL"),
(CommandButtonProcessor, "ID_MAKE_ATTACHMENT IDC_NAME_LIST IDD_SYNC", MakeAttachment, ()),
(CommandButtonProcessor, "ID_CREATE_CASE ID_ATT_METHOD_DROPDOWNLIST IDC_NAME_LIST IDD_SYNC", CreateCase, ()),
(ListBoxProcessor, "IDC_NAME_LIST", SetNameColumn, ())
(ListBoxProcessor, "IDC_NAME_LIST", SetNameColumn, ()),
(TextProcessor, "ID_SEARCH_TEXT ID_SEARCH_TEXT IDC_NAME_LIST", GetSearchText, ()),
),
"IDD_NEW_CONTACT_DIALOG" : (

View File

@ -59,7 +59,7 @@ class XMLRpcConn(object):
'ArchiveToOpenERP', 'IsCRMInstalled', 'GetPartners', 'GetObjectItems', \
'CreateCase', 'MakeAttachment', 'CreateContact', 'CreatePartner', 'getitem', 'setitem', \
'SearchPartnerDetail', 'WritePartnerValues', 'GetAllState', 'GetAllCountry', 'SearchPartner', 'SearchEmailResources', \
'GetCountry', 'GetStates', 'FindCountryForState','CreateEmailAttachment']
'GetCountry', 'GetStates', 'FindCountryForState','CreateEmailAttachment','SearchPartners']
_reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
_reg_clsid_ = "{C6399AFD-763A-400F-8191-7F9D0503CAE2}"
_reg_progid_ = "Python.OpenERP.XMLRpcConn"
@ -550,3 +550,14 @@ class XMLRpcConn(object):
res['res_id'] = obj_id
id = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,'ir.attachment','create',res)
return id
def SearchPartners(self):
res = []
conn = xmlrpclib.ServerProxy(self._uri+ '/xmlrpc/object')
obj = 'res.partner'
ids = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,obj,'search',[])
recs = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,obj,'read',ids,['id','name'])
for rec in recs:
name = ustr(rec['name'])
res.append((obj,rec['id'],name,obj))
return res

View File

@ -988,7 +988,6 @@ class pos_order_line(osv.osv):
else:
res[line.id][f] = line.price_unit * line.qty
elif f == 'price_subtotal_incl':
tax_amount = 0.0
taxes = [t for t in line.product_id.taxes_id]
if line.qty == 0.0:
res[line.id][f] = 0.0

View File

@ -20,7 +20,7 @@
<field name="name" >Product multi-company</field>
<field name="model_id" ref="model_product_template"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
<field name="domain_force">['|','|',('company_id','=',False),('company_id','child_of',[user.company_id.id]),('company_id.child_ids','child_of',[user.company_id.id])]</field>
</record>
<record id="group_product_variant" model="res.groups" context="{'noadmin':True}">

View File

@ -129,6 +129,7 @@ class project(osv.osv):
'priority': fields.integer('Sequence', help="Gives the sequence order when displaying a list of task"),
'warn_manager': fields.boolean('Warn Manager', help="If you check this field, the project manager will receive a request each time a task is completed by his team.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members', help="Project's member. Not used in any computation, just for information purpose.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'parent_id': fields.many2one('project.project', 'Parent Project'),
'tasks': fields.one2many('project.task', 'project_id', "Project tasks"),
'planned_hours': fields.function(_progress_rate, multi="progress", method=True, string='Planned Time', help="Sum of planned hours of all tasks related to this project and its child projects.",
store = {
@ -342,7 +343,16 @@ class task(osv.osv):
def onchange_planned(self, cr, uid, ids, planned = 0.0, effective = 0.0):
return {'value':{'remaining_hours': planned - effective}}
def onchange_project(self, cr, uid, id, project_id):
if not project_id:
return {}
data = self.pool.get('project.project').browse(cr, uid, [project_id])
partner_id=data and data[0].parent_id.partner_id
if partner_id:
return {'value':{'partner_id':partner_id.id}}
return {}
def _default_project(self, cr, uid, context=None):
if context is None:
context = {}
@ -358,7 +368,7 @@ class task(osv.osv):
default['active'] = True
default['type_id'] = False
if not default.get('name', False):
default['name'] = self.browse(cr, uid, id, context=context).name + _(' (copy)')
default['name'] = self.browse(cr, uid, id, context=context).name
return super(task, self).copy_data(cr, uid, id, default, context)
def _check_dates(self, cr, uid, ids, context=None):
@ -476,6 +486,7 @@ class task(osv.osv):
# Override view according to the company definition
#
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
users_obj = self.pool.get('res.users')

View File

@ -17,12 +17,13 @@
<!--
Resource: project.project
-->
<record id="all_projects_account" model="account.analytic.account">
<field name="name">Projects</field>
<field name="code">3</field>
</record>
<function eval="('default',False,'parent_id', [('project.project', False)], all_projects_account, True, False, False, False, True)" id="parent_project_default_set" model="ir.values" name="set"/>
-->
</data>
</openerp>

View File

@ -241,6 +241,11 @@
</record>
<!-- Projects -->
<record id="all_projects_account" model="project.project">
<field name="name">Projects</field>
<field name="code">3</field>
</record>
<record id="project_project_9" model="project.project">
<field name="warn_manager">1</field>
<field name="name">OpenERP Integration</field>

View File

@ -21,7 +21,7 @@
<form string="Project">
<group colspan="6" col="6">
<field name="name" string="Project Name" select="1"/>
<field name="parent_id" string="Parent Project"/>
<field name="parent_id" domain="[('id','!=',active_id)]"/>
<field name="user_id" string="Project Manager" select="1" attrs="{'readonly':[('state','in',['close', 'cancelled'])]}"/>
<field name="date_start" string="Start Date" attrs="{'readonly':[('state','in',['close', 'cancelled'])]}"/>
<field name="date" string="End Date" attrs="{'readonly':[('state','in',['close', 'cancelled'])]}"/>
@ -211,7 +211,7 @@
<form string="Task edition">
<group colspan="6" col="6">
<field name="name" select="1"/>
<field name="project_id" select="1" domain="[('user_id','=',uid)]"/>
<field name="project_id" select="1" on_change="onchange_project(project_id)" domain="[('user_id','=',uid)]"/>
<field name="total_hours" widget="float_time"/>
<field name="date_deadline" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/>
<field name="user_id" select="1" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/>

View File

@ -3,7 +3,17 @@
<data>
<!-- Partners inherited form -->
<record id="base.view_crm_partner_info_History" model="ir.ui.view">
<field name="name">res.partner.crm.history.inherit1</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="/form/notebook/page[@string='History']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
</field>
</record>
<record id="view_task_partner_info_form" model="ir.ui.view">
<field name="name">res.partner.task.info.inherit</field>
<field name="model">res.partner</field>

View File

@ -57,7 +57,7 @@
</field>
</field>
</record>
<record id="view_project_caldav_task_form2" model="ir.ui.view">
<field name="name">project.task.caldav.form2</field>
<field name="model">project.task</field>
@ -65,64 +65,70 @@
<field name="type">form</field>
<field name="arch" type="xml">
<field name="progress" position="after">
<group colspan="2" col="4" >
<field name="rrule_type" string="Recurrency" colspan="1" attrs="{'readonly':[('recurrent_uid','!=',False)]}"/>
<button string="Edit All"
help="Edit all Ourrences of recurrent Task"
attrs="{'invisible':[('rrule_type','in', ('none', False))]}"
name="open_task" icon="gtk-edit"
type="object" />
<button string="Exclude range" groups="base.group_extended"
help="Add Exception Rule"
name="%(base_calendar.action_base_calendar_set_exrule)d" icon="gtk-zoom-out" type="action"
context="{'model' : 'project.task'}"
attrs="{'invisible':[('rrule_type','in', ('none', False))]}"/>
</group>
<newline/>
<group col="4" colspan="6" name="rrule" attrs="{'invisible': [('rrule_type','!=','custom')]}">
<separator string="Custom Recurrency Rule" colspan="8"/>
<group col="8" colspan="4">
<field name="interval" />
<field name="freq" />
<field name="count" />
<field name="end_date" />
</group>
<group col="14" colspan="4" name="Select weekdays"
attrs="{'invisible': [('freq','=','weekly')]}">
<field name="mo" colspan="1" />
<field name="tu" colspan="1" />
<field name="we" colspan="1" />
<field name="th" colspan="1" />
<field name="fr" colspan="1" />
<field name="sa" colspan="1" />
<field name="su" colspan="1" />
<newline />
</group>
<group col="10" colspan="4" attrs="{'invisible': [('freq','!=','monthly'), ('freq','!=','yearly')]}">
<group col="2" colspan="1">
<field name="select1" />
</group>
<group col="2" colspan="1" attrs="{'invisible' : [('select1','=','day')]}">
<field name="day" attrs="{'required' : [('select1','=','date')]}" />
</group>
<group col="3" colspan="1" attrs="{'invisible' : [('select1','=','date')]}">
<field name="byday" string="The" attrs="{'required' : [('select1','=','day')]}" />
<field name="week_list" nolabel="1" attrs="{'required' : [('select1','=','day')]}" />
</group>
<group col="1" colspan="1" attrs="{'invisible' : [('freq','!=','yearly')]}">
<field name="month_list" string="of" colspan="1" attrs="{'required' : [('freq','=','yearly')]}" />
</group>
</group>
</group>
<field name="recurrency"/>
<field name="edit_all" attrs="{'invisible':[('recurrency','=', False)]}"
on_change="onchange_edit_all(rrule_type,edit_all)"/>
</field>
</field>
</record>
<record id="view_project_caldav_task_form3" model="ir.ui.view">
<record id="view_project_caldav_task_form3" model="ir.ui.view">
<field name="name">project.task.caldav.form3</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2" />
<field name="type">form</field>
<field name="arch" type="xml">
<notebook colspan="4" position="inside">
<page string="Recurrency Option" attrs="{'invisible':[('recurrency','=',False)]}">
<group colspan="2" col="4" >
<field name="rrule_type" string="Recurrency" colspan="1" attrs="{'readonly':[('recurrent_uid','!=',False)]}"/>
</group>
<newline/>
<group col="4" colspan="6" name="rrule">
<separator string="Recurrency Rule" colspan="8"/>
<group col="6" colspan="4">
<field name="interval" string="Repeat Times" attrs="{'readonly': [('end_date','!=',False)]}"/>
<field name="count" attrs="{'readonly': [('end_date','!=',False)]}"/>
<field name="end_date" attrs="{'invisible': [('readonly','!=',False)]}"/>
</group>
<group col="14" colspan="4" name="Select weekdays"
attrs="{'invisible' :[('rrule_type','not in', ['weekly','daily_working'])]}">
<field name="mo" colspan="1" />
<field name="tu" colspan="1" />
<field name="we" colspan="1" />
<field name="th" colspan="1" />
<newline/>
<field name="fr" colspan="1" />
<field name="sa" colspan="1" attrs="{'invisible': [('rrule_type','=','daily_working')]}"/>
<field name="su" colspan="1" attrs="{'invisible': [('rrule_type','=','daily_working')]}"/>
<newline />
</group>
<group col="10" colspan="4" attrs="{'invisible': [('rrule_type','!=','monthly'), ('rrule_type','!=','yearly')]}">
<group col="2" colspan="1">
<field name="select1" />
</group>
<group col="2" colspan="1" attrs="{'invisible' : [('select1','=','day')]}">
<field name="day" attrs="{'required' : [('select1','=','date')]}" />
</group>
<group col="3" colspan="1" attrs="{'invisible' : [('select1','=','date')]}">
<field name="byday" string="The" attrs="{'required' : [('select1','=','day')]}" />
<field name="week_list" nolabel="1" attrs="{'required' : [('select1','=','day')]}" />
</group>
<group col="1" colspan="1" attrs="{'invisible' : [('rrule_type','!=','yearly')]}">
<field name="month_list" string="of" colspan="1" attrs="{'required' : [('rrule_type','=','yearly')]}" />
</group>
</group>
</group>
</page>
</notebook>
</field>
</record>
<record id="view_project_caldav_task_form4" model="ir.ui.view">
<field name="name">project.task.caldav.form4</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2" />
<field name="type">form</field>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="vtimezone" colspan="2" />

View File

@ -151,6 +151,7 @@
<page string="Task Detail">
<field colspan="4" name="task_ids" context="{'default_project_id' :project_id}" nolabel="1">
<tree editable="bottom" string="Project's Tasks">
<field name="sequence"/>
<field name="name"/>
<field name="user_id"/>
<field name="planned_hours" widget="float_time"/>
@ -197,9 +198,9 @@
<field name="sequence"/>
</group>
<separator colspan="4" string="Previous Phases"/>
<field colspan="4" name="previous_phase_ids" nolabel="1"/>
<field colspan="4" name="previous_phase_ids" nolabel="1" domain="[('id','!=',active_id)]"/>
<separator colspan="4" string="Next Phases"/>
<field colspan="4" name="next_phase_ids" nolabel="1"/>
<field colspan="4" name="next_phase_ids" nolabel="1" domain="[('id','!=',active_id)]"/>
</page>
</notebook>
<newline/>

View File

@ -220,4 +220,15 @@ class task(osv.osv):
return super(task,self).write(cr, uid, ids, vals, context)
task()
class res_partner(osv.osv):
_inherit = 'res.partner'
def unlink(self, cursor, user, ids, context=None):
parnter_id=self.pool.get('project.project').search(cursor, user, [('partner_id', 'in', ids)])
if parnter_id:
raise osv.except_osv(_('Invalid action !'), _('Cannot delete Partner which is Assigned to project !'))
return super(res_partner,self).unlink(cursor, user, ids,
context=context)
res_partner()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -123,6 +123,11 @@
<field name="search_view_id" ref="view_account_analytic_line_filter_inherit_buttons"/>
<field name="help">This lists show you every task work you can invoice to the customer. Select the lines in order to generate the invoices automatically.</field>
</record>
<menuitem action="hr_timesheet.action_hr_timesheet_sign_in"
id="menu_hr_timesheet_sign_in"
groups="base.group_extended"
parent="hr_attendance.menu_hr_attendance"
sequence="5" />
<menuitem id="menu_project_billing" name="Invoicing"
parent="base.menu_main_pm" sequence="5"/>

View File

@ -31,7 +31,7 @@
properly.
The Plugin allows you archive email and its attachments to the selected
OpenERP objects. You can select a partner, a task, a project, an analytical
account,or any other object and attach selected mail as .eml file in
account,or any other object and attach selected mail as eml file in
attachment of selected record. You can create Documents for crm Lead,
HR Applicant and project issue from selected mails.

View File

@ -41,7 +41,7 @@ class thunderbird_installer(osv.osv_memory):
_columns = {
'name':fields.char('File name', size=34),
'pdf_name':fields.char('File name', size=64),
'thunderbird':fields.boolean('Thunderbird Plug-in', help="Allows you to select an object that youd like to add to your email and its attachments."),
'thunderbird':fields.boolean('Thunderbird Plug-in', help="Allows you to select an object that you would like to add to your email and its attachments."),
'plugin_file':fields.binary('Thunderbird Plug-in', readonly=True, help="Thunderbird plug-in file. Save as this file and install this plug-in in thunderbird."),
'pdf_file':fields.char('Installation Manual', size="264", help="The documentation file :- how to install Thunderbird Plug-in.", readonly=True),
'description':fields.text('Description', readonly=True)

View File

@ -4,7 +4,7 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="&title.label;" onload="myPrefObserver.register();myPrefObserver.webregister();" unload="myPrefObserver.unregister()" height="410" width="900">
title="&title.label;" onload="myPrefObserver.register();myPrefObserver.webregister();" unload="myPrefObserver.unregister()" height="430" width="900">
<script type="text/javascript" src="chrome://openerp_plugin/content/tiny_xmlrpc.js"></script>
<script type="text/javascript" src="chrome://openerp_plugin/content/config.js"></script>
<script type="text/javascript" src="chrome://openerp_plugin/content/loaddata.js"></script>
@ -16,20 +16,20 @@
<tab label="About"/>
</tabs>
<tabpanels>
<tabpanel id="configtab">
<vbox style="border:1px solid black">
<groupbox id="gpConnection" align ="center" >
<separator class="groove-thin" orient="horizontal" width="94"/>
<caption label="&gpConnection.label;"/>
<tabpanel id="configtab">
<vbox style="border:1px solid black">
<groupbox id="gpConnection" align ="center" >
<separator class="groove-thin" orient="horizontal" width="94"/>
<caption label="&gpConnection.label;"/>
<hbox>
<label align="right" id="url" value="&txturl.label;" width="80" />
<textbox id="txturl" width="200" readonly="true" />
<button label="&getdblist.label;" oncommand="openConfigChange();" image="&imagesearch.value;" width="90"/>
</hbox>
</hbox>
<hbox align="center" id="database_option">
</hbox>
<hbox id="first">
<label align="right" id="lbldb_list1" control="DBlist" value="&database.label;" width="80"/>
<label align="right" id="lbldb_list1" control="DBlist" value="&database.label;" width="80"/>
<textbox id="DBlist_text" width="300"/>
</hbox>
@ -43,33 +43,33 @@
</hbox>
<hbox >
<spacer width="290"/>
<button align="center" id="btconnection" label="&connection.label;" oncommand="testConnection();" image="&imageok.value;" width="100"/>
</hbox>
<button align="center" id="btconnection" label="&connection.label;" oncommand="testConnection();" image="&imageok.value;" width="100"/>
</hbox>
<separator class="groove-thin" orient="horizontal" width="94"/>
</groupbox>
</vbox>
<separator class="groove-thin" orient="horizontal" width="10"/>
<vbox style="border:1px solid black" width="94">
<groupbox id="webgroup" >
<vbox>
<caption label="&webConnection.label;"/>
</vbox>
<separator class="groove-thin" orient="horizontal" width="10"/>
<hbox>
</groupbox>
</vbox>
<separator class="groove-thin" orient="horizontal" width="10"/>
<vbox style="border:1px solid black" width="94">
<groupbox id="webgroup" >
<vbox>
<caption label="&webConnection.label;"/>
</vbox>
<separator class="groove-thin" orient="horizontal" width="10"/>
<hbox>
<label align="right" id="url" value="&txtweburl.label;" width="80" />
<textbox id="txtweburl" width="200" readonly="true"/>
</hbox>
<textbox id="txtweburl" width="200" readonly="true"/>
</hbox>
<hbox >
<spacer width="113"/>
<button align="center" id="websetconnection" label="&setconnection.label;" oncommand="openConfigChangeweb();" image="&imagesearch.value;"/>
<button align="center" id="webopenconnection" label="&openconnection.label;" oncommand="testConnection_web();" image="&imageok.value;"/>
</hbox>
</groupbox>
</vbox>
</tabpanel>
<button align="center" id="websetconnection" label="&setconnection.label;" oncommand="openConfigChangeweb();" image="&imagesearch.value;"/>
<button align="center" id="webopenconnection" label="&openconnection.label;" oncommand="testConnection_web();" image="&imageok.value;"/>
</hbox>
</groupbox>
</vbox>
</tabpanel>
<tabpanel id="objecttab">
<groupbox id="gpObject" width="700" >
<caption label="&listDocument.header;"/>
@ -88,7 +88,7 @@
<button id="btobjectdelete" label="&documentdelete.label;" oncommand="deleteDocument();" image="&imagecancel.value;"/>
</hbox>
<listbox id="listObjectListBox" flex="1" rows="10">
<listbox id="listObjectListBox" flex="1" rows="12">
<listhead>
<listheader label="Title"/>
<listheader label="&listDocumentListBox.header;"/>
@ -110,20 +110,24 @@
<tabpanel id="abouttab">
<groupbox id="gpAbout" width="770" align="center">
<caption label="&gpAbout.label;"/>
<image src="chrome://openerp_plugin/skin/developped_by.png" sizemode="stretch" align="center"/>
<description>&openerp.paresent;</description>
<image src="chrome://openerp_plugin/skin/logo.png" sizemode="stretch" align="center"/>
<description> </description>
<description> </description>
<description>&develop.value;</description>
<image src="chrome://openerp_plugin/skin/developped_by.png" sizemode="stretch" align="center"/>
<description> </description>
<description> </description>
<description>&information.value;</description>
<description><html:a href="&openerp.value;">&openerp.value;</html:a></description>
<description><html:a href="&axelor.value;">&axelor.value;</html:a></description>
<description><html:a href="&tinyerp.value;">&tinyerp.value;</html:a></description>
<description> </description>
<description> </description>
<description>Copyright © 2006-TODAY OpenERP SA All Rights Reserved.</description>
<description>OpenERP is a trademark of the OpenERP SA Company. OpenERP Web is jointly developed by OpenERP SA and Axelor.</description>
<description>OpenERP is a trademark of the OpenERP SA Company. OpenERP Web is jointly developed by OpenERP SA &amp; Axelor.</description>
<description>Licenced under the terms of <html:a href="https://tiny.odoo.com/LICENSE.txt"> OpenERP Public License (OEPL) v1.1 (https://tiny.odoo.com/LICENSE.txt)</html:a> </description>
</groupbox>
</tabpanel>

View File

@ -1,13 +1,13 @@
<!ENTITY title.label "OpenERP Configuration">
<!ENTITY separate.label "Configuration Login for OpenERP!">
<!ENTITY close.label "Close">
<!ENTITY database.label "Database:">
<!ENTITY database.label "Database:">
<!ENTITY ok.label "OK">
<!ENTITY connection.label "Connect">
<!ENTITY setconnection.label "Change">
<!ENTITY connection.label "Connect">
<!ENTITY setconnection.label "Change">
<!ENTITY openconnection.label "Open">
<!ENTITY txturl.label "Server: ">
<!ENTITY txtweburl.label "Server: ">
<!ENTITY txturl.label "Server: ">
<!ENTITY txtweburl.label "Server: ">
<!ENTITY txtwebport.label "Port: ">
<!ENTITY image.label "Image : ">
<!ENTITY obj.label "Document : ">
@ -15,7 +15,7 @@
<!ENTITY password.label "Password: ">
<!ENTITY setdb.label "Move &gt;">
<!ENTITY getdblist.label "Change">
<!ENTITY gpConnection.label "Connection Parameters">
<!ENTITY gpConnection.label "Connection Parameters">
<!ENTITY webConnection.label "Webserver Parameters">
<!ENTITY listDBListBox.header "Available DBs">
<!ENTITY listDocument.header "Document">
@ -35,13 +35,15 @@ Licenced under the terms of OpenERP Public License (OEPL) v1.1 ">
<!ENTITY axelor.value "http://www.axelor.com">
<!ENTITY tinyerp.value "http://tiny.be/">
<!ENTITY openerp.paresent "This Thunderbird Plugin for OpenERP presently (2010-today) developed by OpenERP SA.">
<!ENTITY openerp_s.value "OpenERP">
<!ENTITY axelor_s.value "The Axelor Company">
<!ENTITY tinyerp_s.value "The Tiny Company">
<!ENTITY imageicon.value "chrome://openerp_plugin/skin/NEWT1.png">
<!ENTITY gpAbout.label "About OpenERP Thunderbird Plugin">
<!ENTITY develop.value "This Thunderbird Plugin for OpenERP has been developed by OpenERP SA &amp; Axelor">
<!ENTITY develop.value "Based on original work, copyright 2003-2009, by Tiny &amp; Axelor ">
<!ENTITY information.value "For more information, please visit our website">
<!ENTITY contact.label "Contact Us">
<!ENTITY contact.value "info@axelor.com">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB