[MERGE] forward port of branch 7.0 up to revid 5224 chs@openerp.com-20140206105141-7e8bv8ordqqsswh7
bzr revid: dle@openerp.com-20140205105045-j95kduyjiq83e57z bzr revid: dle@openerp.com-20140114173613-ruxye1m7fxtcjfim bzr revid: dle@openerp.com-20140116161158-u95vcs3os4tk2zob bzr revid: dle@openerp.com-20140121172737-gzawfi3ssg7xifef bzr revid: chs@openerp.com-20140129094554-c3abc8x3qz9mpszr bzr revid: chs@openerp.com-20140129140159-9vwkpr078shoonum bzr revid: chs@openerp.com-20140206110220-myn57cryam1y4k9v
This commit is contained in:
commit
a69f789b41
|
@ -114,7 +114,7 @@ class account_invoice(osv.osv):
|
||||||
#we check if the invoice is partially reconciled and if there are other invoices
|
#we check if the invoice is partially reconciled and if there are other invoices
|
||||||
#involved in this partial reconciliation (and we sum these invoices)
|
#involved in this partial reconciliation (and we sum these invoices)
|
||||||
for line in aml.reconcile_partial_id.line_partial_ids:
|
for line in aml.reconcile_partial_id.line_partial_ids:
|
||||||
if line.invoice:
|
if line.invoice and invoice.type == line.invoice.type:
|
||||||
nb_inv_in_partial_rec += 1
|
nb_inv_in_partial_rec += 1
|
||||||
#store the max invoice id as for this invoice we will make a balance instead of a simple division
|
#store the max invoice id as for this invoice we will make a balance instead of a simple division
|
||||||
max_invoice_id = max(max_invoice_id, line.invoice.id)
|
max_invoice_id = max(max_invoice_id, line.invoice.id)
|
||||||
|
|
|
@ -268,10 +268,14 @@ def log_fct(cr, uid_orig, model, method, fct_src, *args, **kw):
|
||||||
new_values = get_data(cr, uid_orig, pool, res_ids, model, method)
|
new_values = get_data(cr, uid_orig, pool, res_ids, model, method)
|
||||||
elif method == 'read':
|
elif method == 'read':
|
||||||
res = fct_src(cr, uid_orig, model.model, method, *args, **kw)
|
res = fct_src(cr, uid_orig, model.model, method, *args, **kw)
|
||||||
|
if isinstance(res, dict):
|
||||||
|
records = [res]
|
||||||
|
else:
|
||||||
|
records = res
|
||||||
# build the res_ids and the old_values dict. Here we don't use get_data() to
|
# build the res_ids and the old_values dict. Here we don't use get_data() to
|
||||||
# avoid performing an additional read()
|
# avoid performing an additional read()
|
||||||
res_ids = []
|
res_ids = []
|
||||||
for record in res:
|
for record in records:
|
||||||
res_ids.append(record['id'])
|
res_ids.append(record['id'])
|
||||||
old_values[(model.id, record['id'])] = {'value': record, 'text': record}
|
old_values[(model.id, record['id'])] = {'value': record, 'text': record}
|
||||||
# log only the fields read
|
# log only the fields read
|
||||||
|
@ -279,7 +283,9 @@ def log_fct(cr, uid_orig, model, method, fct_src, *args, **kw):
|
||||||
elif method == 'unlink':
|
elif method == 'unlink':
|
||||||
res_ids = args[0]
|
res_ids = args[0]
|
||||||
old_values = get_data(cr, uid_orig, pool, res_ids, model, method)
|
old_values = get_data(cr, uid_orig, pool, res_ids, model, method)
|
||||||
res = fct_src(cr, uid_orig, model.model, method, *args, **kw)
|
# process_data first as fct_src will unlink the record
|
||||||
|
self.process_data(cr, uid_orig, pool, res_ids, model, method, old_values, new_values, field_list)
|
||||||
|
return fct_src(cr, uid_orig, model.model, method, *args, **kw)
|
||||||
else: # method is write, action or workflow action
|
else: # method is write, action or workflow action
|
||||||
res_ids = []
|
res_ids = []
|
||||||
if args:
|
if args:
|
||||||
|
@ -322,7 +328,7 @@ def get_data(cr, uid, pool, res_ids, model, method):
|
||||||
data = {}
|
data = {}
|
||||||
resource_pool = pool[model.model]
|
resource_pool = pool[model.model]
|
||||||
# read all the fields of the given resources in super admin mode
|
# read all the fields of the given resources in super admin mode
|
||||||
for resource in resource_pool.read(cr, SUPERUSER_ID, res_ids):
|
for resource in resource_pool.read(cr, SUPERUSER_ID, res_ids, resource_pool._all_columns):
|
||||||
values = {}
|
values = {}
|
||||||
values_text = {}
|
values_text = {}
|
||||||
resource_id = resource['id']
|
resource_id = resource['id']
|
||||||
|
@ -456,7 +462,9 @@ def process_data(cr, uid, pool, res_ids, model, method, old_values=None, new_val
|
||||||
|
|
||||||
# if at least one modification has been found
|
# if at least one modification has been found
|
||||||
for model_id, resource_id in lines:
|
for model_id, resource_id in lines:
|
||||||
name = pool[model.model].name_get(cr, uid, [resource_id])[0][1]
|
line_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, model_id).model
|
||||||
|
name = pool.get(line_model).name_get(cr, uid, [resource_id])[0][1]
|
||||||
|
|
||||||
vals = {
|
vals = {
|
||||||
'method': method,
|
'method': method,
|
||||||
'object_id': model_id,
|
'object_id': model_id,
|
||||||
|
|
|
@ -133,14 +133,14 @@
|
||||||
<t t-if="widget.is_private or (widget.user_pid != partner[0])">
|
<t t-if="widget.is_private or (widget.user_pid != partner[0])">
|
||||||
<t t-if="!widget.is_private and inc==0"> and </t>
|
<t t-if="!widget.is_private and inc==0"> and </t>
|
||||||
<span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/>
|
<span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/>
|
||||||
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-raw="partner[1]"/></a>
|
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-esc="partner[1]"/></a>
|
||||||
<t t-if="!widget.options.show_link" t-raw="partner[1]"/>
|
<t t-if="!widget.options.show_link" t-esc="partner[1]"/>
|
||||||
</span>
|
</span>
|
||||||
<t t-set="inc" t-value="inc+1"/>
|
<t t-set="inc" t-value="inc+1"/>
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
<t t-if="widget.partner_ids.length > 3">
|
<t t-if="widget.partner_ids.length > 3">
|
||||||
<span class="oe_more">, <a><t t-raw="widget.partner_ids.length - 3"/> others...</a></span>
|
<span class="oe_more">, <a><t t-esc="widget.partner_ids.length - 3"/> others...</a></span>
|
||||||
<a class="oe_more_hidden"><<<</a>
|
<a class="oe_more_hidden"><<<</a>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
|
@ -148,8 +148,8 @@
|
||||||
<t t-foreach='widget.recipients' t-as='recipient'>
|
<t t-foreach='widget.recipients' t-as='recipient'>
|
||||||
<label t-attf-title="Add as recipient and follower (reason: #{recipient.reason})">
|
<label t-attf-title="Add as recipient and follower (reason: #{recipient.reason})">
|
||||||
<input type="checkbox" t-att-checked="recipient.checked ? 'checked' : undefined" t-att-data="recipient.full_name"/>
|
<input type="checkbox" t-att-checked="recipient.checked ? 'checked' : undefined" t-att-data="recipient.full_name"/>
|
||||||
<t t-raw="recipient.name"/>
|
<t t-esc="recipient.name"/>
|
||||||
<t t-if="recipient.email_address">(<t t-raw="recipient.email_address"/>)</t>
|
<t t-if="recipient.email_address">(<t t-esc="recipient.email_address"/>)</t>
|
||||||
<t t-if="!recipient.email_address">(no email address)</t>
|
<t t-if="!recipient.email_address">(no email address)</t>
|
||||||
</label>
|
</label>
|
||||||
</t>
|
</t>
|
||||||
|
@ -175,7 +175,7 @@
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<h2 class="oe_view_title">
|
<h2 class="oe_view_title">
|
||||||
<span class="oe_view_title_text">
|
<span class="oe_view_title_text">
|
||||||
<t t-raw="widget.action.name"/>
|
<t t-esc="widget.action.name"/>
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<t t-if="widget.action.params.header_description">
|
<t t-if="widget.action.params.header_description">
|
||||||
|
@ -261,11 +261,11 @@
|
||||||
<div class="oe_msg_content">
|
<div class="oe_msg_content">
|
||||||
<h1 t-if="(widget.show_record_name or widget.subject) and !widget.thread_level" class="oe_msg_title">
|
<h1 t-if="(widget.show_record_name or widget.subject) and !widget.thread_level" class="oe_msg_title">
|
||||||
<a t-if="widget.options.show_link and widget.show_record_name" class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&id=#{widget.res_id}">
|
<a t-if="widget.options.show_link and widget.show_record_name" class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&id=#{widget.res_id}">
|
||||||
<t t-raw="widget.record_name"/>
|
<t t-esc="widget.record_name"/>
|
||||||
</a>
|
</a>
|
||||||
<span t-if="!widget.options.show_link and widget.show_record_name"><t t-raw="widget.record_name"/></span>
|
<span t-if="!widget.options.show_link and widget.show_record_name"><t t-esc="widget.record_name"/></span>
|
||||||
<t t-if="widget.show_record_name and widget.subject">: </t>
|
<t t-if="widget.show_record_name and widget.subject">: </t>
|
||||||
<t t-if="widget.subject" t-raw="widget.subject"/>
|
<t t-if="widget.subject" t-esc="widget.subject"/>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="oe_msg_body">
|
<div class="oe_msg_body">
|
||||||
<t t-if="widget.body_short">
|
<t t-if="widget.body_short">
|
||||||
|
@ -281,8 +281,8 @@
|
||||||
<t t-if="widget.attachment_ids.length > 0">
|
<t t-if="widget.attachment_ids.length > 0">
|
||||||
<div class="oe_msg_attachment_list"></div>
|
<div class="oe_msg_attachment_list"></div>
|
||||||
</t>
|
</t>
|
||||||
<a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-raw="widget.author_id[2]"/></a>
|
<a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-esc="widget.author_id[2]"/></a>
|
||||||
<span t-if="widget.author_id and (!widget.options.show_link or !widget.author_id[0])"><t t-raw="widget.author_id[2]"/></span>
|
<span t-if="widget.author_id and (!widget.options.show_link or !widget.author_id[0])"><t t-esc="widget.author_id[2]"/></span>
|
||||||
<t t-if="widget.type == 'notification'">
|
<t t-if="widget.type == 'notification'">
|
||||||
updated document
|
updated document
|
||||||
<t t-if="widget.partner_ids.length > 0">
|
<t t-if="widget.partner_ids.length > 0">
|
||||||
|
@ -301,20 +301,20 @@
|
||||||
<t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))"
|
<t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))"
|
||||||
t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
|
t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
|
||||||
<span t-attf-class="oe_partner_follower">
|
<span t-attf-class="oe_partner_follower">
|
||||||
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-raw="partner[1]"/></a>
|
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-esc="partner[1]"/></a>
|
||||||
<t t-if="!widget.options.show_link" t-raw="partner[1]"/>
|
<t t-if="!widget.options.show_link" t-esc="partner[1]"/>
|
||||||
</span>
|
</span>
|
||||||
<t t-if="!partner_last">,</t>
|
<t t-if="!partner_last">,</t>
|
||||||
</t>
|
</t>
|
||||||
<t t-if="widget.partner_ids.length > 3">
|
<t t-if="widget.partner_ids.length > 3">
|
||||||
<span t-att-title="widget.extra_partners_str">and <t t-raw="widget.extra_partners_nbr"/> more</span>
|
<span t-att-title="widget.extra_partners_str">and <t t-esc="widget.extra_partners_nbr"/> more</span>
|
||||||
</t>
|
</t>
|
||||||
<t t-if="widget.type == 'notification' and widget.partner_ids.length > 0">
|
<t t-if="widget.type == 'notification' and widget.partner_ids.length > 0">
|
||||||
notified
|
notified
|
||||||
</t>
|
</t>
|
||||||
<span class='oe_subtle'>•</span>
|
<span class='oe_subtle'>•</span>
|
||||||
<span t-att-title="widget.date">
|
<span t-att-title="widget.date">
|
||||||
<t t-if="widget.timerelative" t-raw="widget.timerelative"/>
|
<t t-if="widget.timerelative" t-esc="widget.timerelative"/>
|
||||||
<t t-if="!widget.timerelative" t-raw="widget.display_date"/>
|
<t t-if="!widget.timerelative" t-raw="widget.display_date"/>
|
||||||
</span>
|
</span>
|
||||||
<span t-if="!widget.options.readonly" class='oe_subtle'>•</span>
|
<span t-if="!widget.options.readonly" class='oe_subtle'>•</span>
|
||||||
|
@ -331,7 +331,7 @@
|
||||||
<div class='oe_separator'></div>
|
<div class='oe_separator'></div>
|
||||||
<a t-if="widget.nb_messages <= 0" class="oe_msg_fetch_more">show more message</a>
|
<a t-if="widget.nb_messages <= 0" class="oe_msg_fetch_more">show more message</a>
|
||||||
<a t-if="widget.nb_messages === 1" class="oe_msg_fetch_more">show one more message</a>
|
<a t-if="widget.nb_messages === 1" class="oe_msg_fetch_more">show one more message</a>
|
||||||
<a t-if="widget.nb_messages > 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a>
|
<a t-if="widget.nb_messages > 1" class="oe_msg_fetch_more">show <t t-esc="widget.nb_messages" /> more messages</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
@ -351,7 +351,7 @@
|
||||||
-->
|
-->
|
||||||
<span t-name="mail.thread.message.vote">
|
<span t-name="mail.thread.message.vote">
|
||||||
<span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
|
<span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
|
||||||
<t t-raw='widget.vote_nb' />
|
<t t-esc='widget.vote_nb' />
|
||||||
<span class='oe_e'>8</span>
|
<span class='oe_e'>8</span>
|
||||||
</span>
|
</span>
|
||||||
<a href='#' class="oe_msg_vote">
|
<a href='#' class="oe_msg_vote">
|
||||||
|
|
|
@ -43,13 +43,13 @@
|
||||||
<table class='oe_subtype'>
|
<table class='oe_subtype'>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="10%"><input type="checkbox" t-att-checked="record.followed" t-att-id="'input_mail_followers_subtype_'+record.id" t-att-data-id="record.id" t-att-name="record.name" class="oe_msg_subtype_check"/></td>
|
<td width="10%"><input type="checkbox" t-att-checked="record.followed" t-att-id="'input_mail_followers_subtype_'+record.id" t-att-data-id="record.id" t-att-name="record.name" class="oe_msg_subtype_check"/></td>
|
||||||
<td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-raw="record.name"/></label></td>
|
<td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-esc="record.name"/></label></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<t t-name="mail.followers.show_more">
|
<t t-name="mail.followers.show_more">
|
||||||
<div class="oe_partner oe_show_more">And <t t-raw="number"/> more.</div>
|
<div class="oe_partner oe_show_more">And <t t-esc="number"/> more.</div>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -12,6 +12,7 @@ from openerp.osv import orm
|
||||||
from openerp.tools.translate import _
|
from openerp.tools.translate import _
|
||||||
from openerp.tools.misc import DEFAULT_SERVER_DATE_FORMAT,\
|
from openerp.tools.misc import DEFAULT_SERVER_DATE_FORMAT,\
|
||||||
DEFAULT_SERVER_DATETIME_FORMAT
|
DEFAULT_SERVER_DATETIME_FORMAT
|
||||||
|
from openerp.tools import html_sanitize
|
||||||
|
|
||||||
REFERENCING_FIELDS = set([None, 'id', '.id'])
|
REFERENCING_FIELDS = set([None, 'id', '.id'])
|
||||||
def only_ref_fields(record):
|
def only_ref_fields(record):
|
||||||
|
@ -184,7 +185,7 @@ class ir_fields_converter(orm.Model):
|
||||||
|
|
||||||
def _str_id(self, cr, uid, model, column, value, context=None):
|
def _str_id(self, cr, uid, model, column, value, context=None):
|
||||||
return value, []
|
return value, []
|
||||||
_str_to_reference = _str_to_char = _str_to_text = _str_to_binary = _str_id
|
_str_to_reference = _str_to_char = _str_to_text = _str_to_binary = _str_to_html = _str_id
|
||||||
|
|
||||||
def _str_to_date(self, cr, uid, model, column, value, context=None):
|
def _str_to_date(self, cr, uid, model, column, value, context=None):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
domain="[('comments', 'like', 'openerp-web')]"/>
|
domain="[('comments', 'like', 'openerp-web')]"/>
|
||||||
<field name="name" operator="="/>
|
<field name="name" operator="="/>
|
||||||
<field name="lang"/>
|
<field name="lang"/>
|
||||||
<field name="source"/>
|
<field name="src"/>
|
||||||
<field name="value"/>
|
<field name="value"/>
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
|
|
|
@ -26,7 +26,7 @@ from openerp import report, tools
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
def graph_get(cr, graph, wkf_ids, nested, workitem, witm_trans, processed_subflows):
|
||||||
import pydot
|
import pydot
|
||||||
cr.execute('select * from wkf_activity where wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
|
cr.execute('select * from wkf_activity where wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
|
||||||
nodes = cr.dictfetchall()
|
nodes = cr.dictfetchall()
|
||||||
|
@ -40,7 +40,7 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
||||||
cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
|
cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
|
||||||
wkfinfo = cr.dictfetchone()
|
wkfinfo = cr.dictfetchone()
|
||||||
graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = "\"Subflow: %s\\nOSV: %s\"" % ( n['name'], wkfinfo['osv']) )
|
graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = "\"Subflow: %s\\nOSV: %s\"" % ( n['name'], wkfinfo['osv']) )
|
||||||
(s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, processed_subflows)
|
(s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, witm_trans, processed_subflows)
|
||||||
graph.add_subgraph(graph2)
|
graph.add_subgraph(graph2)
|
||||||
actfrom[n['id']] = s2
|
actfrom[n['id']] = s2
|
||||||
actto[n['id']] = s1
|
actto[n['id']] = s1
|
||||||
|
@ -89,7 +89,9 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
||||||
args['arrowtail']='inv'
|
args['arrowtail']='inv'
|
||||||
|
|
||||||
if activities[t['act_to']]['join_mode']=='AND':
|
if activities[t['act_to']]['join_mode']=='AND':
|
||||||
args['arrowhead']='crow'
|
args['arrowhead']='crow'
|
||||||
|
if t['id'] in witm_trans:
|
||||||
|
args['color'] = 'red'
|
||||||
|
|
||||||
activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0])
|
activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0])
|
||||||
activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
|
activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
|
||||||
|
@ -119,8 +121,12 @@ def graph_instance_get(cr, graph, inst_id, nested=False):
|
||||||
workitems.update(workitem_get(subflow_id))
|
workitems.update(workitem_get(subflow_id))
|
||||||
return workitems
|
return workitems
|
||||||
|
|
||||||
|
def witm_get(instance):
|
||||||
|
cr.execute("select trans_id from wkf_witm_trans where inst_id=%s", (instance,))
|
||||||
|
return set(t[0] for t in cr.fetchall())
|
||||||
|
|
||||||
processed_subflows = set()
|
processed_subflows = set()
|
||||||
graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), processed_subflows)
|
graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), witm_get(inst_id), processed_subflows)
|
||||||
|
|
||||||
#
|
#
|
||||||
# TODO: pas clean: concurrent !!!
|
# TODO: pas clean: concurrent !!!
|
||||||
|
|
|
@ -611,26 +611,36 @@ class res_partner(osv.osv, format_address):
|
||||||
if not args:
|
if not args:
|
||||||
args = []
|
args = []
|
||||||
if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
|
if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
|
||||||
|
|
||||||
|
self.check_access_rights(cr, uid, 'read')
|
||||||
|
where_query = self._where_calc(cr, uid, args, context=context)
|
||||||
|
self._apply_ir_rules(cr, uid, where_query, 'read', context=context)
|
||||||
|
from_clause, where_clause, where_clause_params = where_query.get_sql()
|
||||||
|
where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE '
|
||||||
|
|
||||||
# search on the name of the contacts and of its company
|
# search on the name of the contacts and of its company
|
||||||
search_name = name
|
search_name = name
|
||||||
if operator in ('ilike', 'like'):
|
if operator in ('ilike', 'like'):
|
||||||
search_name = '%%%s%%' % name
|
search_name = '%%%s%%' % name
|
||||||
if operator in ('=ilike', '=like'):
|
if operator in ('=ilike', '=like'):
|
||||||
operator = operator[1:]
|
operator = operator[1:]
|
||||||
query_args = {'name': search_name}
|
|
||||||
query = ('''SELECT id FROM res_partner
|
query = ('SELECT id FROM res_partner ' +
|
||||||
WHERE email ''' + operator + ''' %(name)s
|
where_str + '(email ' + operator + ''' %s
|
||||||
OR display_name ''' + operator + ''' %(name)s
|
OR display_name ''' + operator + ''' %s)
|
||||||
ORDER BY display_name
|
ORDER BY display_name''')
|
||||||
''')
|
|
||||||
|
where_clause_params += [search_name, search_name]
|
||||||
if limit:
|
if limit:
|
||||||
query += ' limit %(limit)s'
|
query += ' limit %s'
|
||||||
query_args['limit'] = limit
|
where_clause_params.append(limit)
|
||||||
cr.execute(query, query_args)
|
cr.execute(query, where_clause_params)
|
||||||
ids = map(lambda x: x[0], cr.fetchall())
|
ids = map(lambda x: x[0], cr.fetchall())
|
||||||
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
|
|
||||||
if ids:
|
if ids:
|
||||||
return self.name_get(cr, uid, ids, context)
|
return self.name_get(cr, uid, ids, context)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
return super(res_partner,self).name_search(cr, uid, name, args, operator=operator, context=context, limit=limit)
|
return super(res_partner,self).name_search(cr, uid, name, args, operator=operator, context=context, limit=limit)
|
||||||
|
|
||||||
def find_or_create(self, cr, uid, email, context=None):
|
def find_or_create(self, cr, uid, email, context=None):
|
||||||
|
|
|
@ -8,6 +8,7 @@ class test_base(common.TransactionCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(test_base,self).setUp()
|
super(test_base,self).setUp()
|
||||||
self.res_partner = self.registry('res.partner')
|
self.res_partner = self.registry('res.partner')
|
||||||
|
self.res_users = self.registry('res.users')
|
||||||
|
|
||||||
# samples use effective TLDs from the Mozilla public suffix
|
# samples use effective TLDs from the Mozilla public suffix
|
||||||
# list at http://publicsuffix.org
|
# list at http://publicsuffix.org
|
||||||
|
@ -39,6 +40,20 @@ class test_base(common.TransactionCase):
|
||||||
new_id2 = self.res_partner.find_or_create(cr, uid, self.samples[2][0])
|
new_id2 = self.res_partner.find_or_create(cr, uid, self.samples[2][0])
|
||||||
self.assertTrue(new_id2 > new_id, 'find_or_create failed - should have created new one again')
|
self.assertTrue(new_id2 > new_id, 'find_or_create failed - should have created new one again')
|
||||||
|
|
||||||
|
def test_15_res_partner_name_search(self):
|
||||||
|
cr,uid = self.cr, self.uid
|
||||||
|
for name, active in [
|
||||||
|
('"A Raoul Grosbedon" <raoul@chirurgiens-dentistes.fr>', False),
|
||||||
|
('B Raoul chirurgiens-dentistes.fr', True),
|
||||||
|
("C Raoul O'hara <!@historicalsociety.museum>", True),
|
||||||
|
('ryu+giga-Sushi@aizubange.fukushima.jp', True),
|
||||||
|
]:
|
||||||
|
partner_id, dummy = self.res_partner.name_create(cr, uid, name, context={'default_active': active})
|
||||||
|
partners = self.res_partner.name_search(cr, uid, 'Raoul')
|
||||||
|
self.assertEqual(len(partners), 2, 'Incorrect search number result for name_search')
|
||||||
|
partners = self.res_partner.name_search(cr, uid, 'Raoul', limit=1)
|
||||||
|
self.assertEqual(len(partners), 1, 'Incorrect search number result for name_search with a limit')
|
||||||
|
self.assertEqual(partners[0][1], 'B Raoul chirurgiens-dentistes.fr', 'Incorrect partner returned, should be the first active')
|
||||||
|
|
||||||
def test_20_res_partner_address_sync(self):
|
def test_20_res_partner_address_sync(self):
|
||||||
cr, uid = self.cr, self.uid
|
cr, uid = self.cr, self.uid
|
||||||
|
@ -268,6 +283,21 @@ class test_base(common.TransactionCase):
|
||||||
p0.refresh()
|
p0.refresh()
|
||||||
self.assertEquals(p0.vat, sunhelmvat2, 'Commercial fields must be automatically synced')
|
self.assertEquals(p0.vat, sunhelmvat2, 'Commercial fields must be automatically synced')
|
||||||
|
|
||||||
|
def test_60_read_group(self):
|
||||||
|
cr, uid = self.cr, self.uid
|
||||||
|
for user_data in [
|
||||||
|
{'name': 'Alice', 'login': 'alice', 'color': 1, 'function': 'Friend'},
|
||||||
|
{'name': 'Bob', 'login': 'bob', 'color': 2, 'function': 'Friend'},
|
||||||
|
{'name': 'Eve', 'login': 'eve', 'color': 3, 'function': 'Eavesdropper'},
|
||||||
|
]:
|
||||||
|
self.res_users.create(cr, uid, user_data)
|
||||||
|
|
||||||
|
groups_data = self.res_users.read_group(cr, uid, domain=[('login', 'in', ('alice', 'bob', 'eve'))], fields=['name', 'color', 'function'], groupby='function')
|
||||||
|
self.assertEqual(len(groups_data), 2, "Incorrect number of results when grouping on a field")
|
||||||
|
for group_data in groups_data:
|
||||||
|
self.assertIn('color', group_data, "Aggregated data for the column 'color' is not present in read_group return values")
|
||||||
|
self.assertEqual(group_data['color'], 3, "Incorrect sum for aggregated data for the column 'color'")
|
||||||
|
|
||||||
class test_partner_recursion(common.TransactionCase):
|
class test_partner_recursion(common.TransactionCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -68,8 +68,8 @@ class Registry(Mapping):
|
||||||
# must be reloaded.
|
# must be reloaded.
|
||||||
# The `base_cache_signaling sequence` indicates all caches must be
|
# The `base_cache_signaling sequence` indicates all caches must be
|
||||||
# invalidated (i.e. cleared).
|
# invalidated (i.e. cleared).
|
||||||
self.base_registry_signaling_sequence = 1
|
self.base_registry_signaling_sequence = None
|
||||||
self.base_cache_signaling_sequence = 1
|
self.base_cache_signaling_sequence = None
|
||||||
|
|
||||||
# Flag indicating if at least one model cache has been cleared.
|
# Flag indicating if at least one model cache has been cleared.
|
||||||
# Useful only in a multi-process context.
|
# Useful only in a multi-process context.
|
||||||
|
@ -159,7 +159,7 @@ class Registry(Mapping):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_multi_process_signaling(cls, cr):
|
def setup_multi_process_signaling(cls, cr):
|
||||||
if not openerp.multi_process:
|
if not openerp.multi_process:
|
||||||
return
|
return None, None
|
||||||
|
|
||||||
# Inter-process signaling:
|
# Inter-process signaling:
|
||||||
# The `base_registry_signaling` sequence indicates the whole registry
|
# The `base_registry_signaling` sequence indicates the whole registry
|
||||||
|
@ -172,6 +172,16 @@ class Registry(Mapping):
|
||||||
cr.execute("""SELECT nextval('base_registry_signaling')""")
|
cr.execute("""SELECT nextval('base_registry_signaling')""")
|
||||||
cr.execute("""CREATE SEQUENCE base_cache_signaling INCREMENT BY 1 START WITH 1""")
|
cr.execute("""CREATE SEQUENCE base_cache_signaling INCREMENT BY 1 START WITH 1""")
|
||||||
cr.execute("""SELECT nextval('base_cache_signaling')""")
|
cr.execute("""SELECT nextval('base_cache_signaling')""")
|
||||||
|
|
||||||
|
cr.execute("""
|
||||||
|
SELECT base_registry_signaling.last_value,
|
||||||
|
base_cache_signaling.last_value
|
||||||
|
FROM base_registry_signaling, base_cache_signaling""")
|
||||||
|
r, c = cr.fetchone()
|
||||||
|
_logger.debug("Multiprocess load registry signaling: [Registry: # %s] "\
|
||||||
|
"[Cache: # %s]",
|
||||||
|
r, c)
|
||||||
|
return r, c
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def cursor(self, auto_commit=True):
|
def cursor(self, auto_commit=True):
|
||||||
|
@ -229,6 +239,10 @@ class RegistryManager(object):
|
||||||
cls.delete(db_name)
|
cls.delete(db_name)
|
||||||
cls.registries[db_name] = registry
|
cls.registries[db_name] = registry
|
||||||
try:
|
try:
|
||||||
|
with registry.cursor() as cr:
|
||||||
|
seq_registry, seq_cache = Registry.setup_multi_process_signaling(cr)
|
||||||
|
registry.base_registry_signaling_sequence = seq_registry
|
||||||
|
registry.base_cache_signaling_sequence = seq_cache
|
||||||
# This should be a method on Registry
|
# This should be a method on Registry
|
||||||
openerp.modules.load_modules(registry.db, force_demo, status, update_module)
|
openerp.modules.load_modules(registry.db, force_demo, status, update_module)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -242,7 +256,6 @@ class RegistryManager(object):
|
||||||
|
|
||||||
cr = registry.db.cursor()
|
cr = registry.db.cursor()
|
||||||
try:
|
try:
|
||||||
Registry.setup_multi_process_signaling(cr)
|
|
||||||
registry.do_parent_store(cr)
|
registry.do_parent_store(cr)
|
||||||
cr.commit()
|
cr.commit()
|
||||||
finally:
|
finally:
|
||||||
|
@ -304,16 +317,20 @@ class RegistryManager(object):
|
||||||
base_cache_signaling.last_value
|
base_cache_signaling.last_value
|
||||||
FROM base_registry_signaling, base_cache_signaling""")
|
FROM base_registry_signaling, base_cache_signaling""")
|
||||||
r, c = cr.fetchone()
|
r, c = cr.fetchone()
|
||||||
|
_logger.debug("Multiprocess signaling check: [Registry - old# %s new# %s] "\
|
||||||
|
"[Cache - old# %s new# %s]",
|
||||||
|
registry.base_registry_signaling_sequence, r,
|
||||||
|
registry.base_cache_signaling_sequence, c)
|
||||||
# Check if the model registry must be reloaded (e.g. after the
|
# Check if the model registry must be reloaded (e.g. after the
|
||||||
# database has been updated by another process).
|
# database has been updated by another process).
|
||||||
if registry.base_registry_signaling_sequence > 1 and registry.base_registry_signaling_sequence != r:
|
if registry.base_registry_signaling_sequence is not None and registry.base_registry_signaling_sequence != r:
|
||||||
changed = True
|
changed = True
|
||||||
_logger.info("Reloading the model registry after database signaling.")
|
_logger.info("Reloading the model registry after database signaling.")
|
||||||
registry = cls.new(db_name)
|
registry = cls.new(db_name)
|
||||||
# Check if the model caches must be invalidated (e.g. after a write
|
# Check if the model caches must be invalidated (e.g. after a write
|
||||||
# occured on another process). Don't clear right after a registry
|
# occured on another process). Don't clear right after a registry
|
||||||
# has been reload.
|
# has been reload.
|
||||||
elif registry.base_cache_signaling_sequence > 1 and registry.base_cache_signaling_sequence != c:
|
elif registry.base_cache_signaling_sequence is not None and registry.base_cache_signaling_sequence != c:
|
||||||
changed = True
|
changed = True
|
||||||
_logger.info("Invalidating all model caches after database signaling.")
|
_logger.info("Invalidating all model caches after database signaling.")
|
||||||
registry.clear_caches()
|
registry.clear_caches()
|
||||||
|
@ -352,6 +369,7 @@ class RegistryManager(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def signal_registry_change(cls, db_name):
|
def signal_registry_change(cls, db_name):
|
||||||
if openerp.multi_process and db_name in cls.registries:
|
if openerp.multi_process and db_name in cls.registries:
|
||||||
|
_logger.info("Registry changed, signaling through the database")
|
||||||
registry = cls.get(db_name)
|
registry = cls.get(db_name)
|
||||||
cr = registry.db.cursor()
|
cr = registry.db.cursor()
|
||||||
r = 1
|
r = 1
|
||||||
|
|
|
@ -1018,7 +1018,7 @@ class expression(object):
|
||||||
right += ' 23:59:59'
|
right += ' 23:59:59'
|
||||||
push(create_substitution_leaf(leaf, (left, operator, right), working_model))
|
push(create_substitution_leaf(leaf, (left, operator, right), working_model))
|
||||||
|
|
||||||
elif field.translate:
|
elif field.translate and right:
|
||||||
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
|
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
|
||||||
sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator)
|
sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator)
|
||||||
if need_wildcard:
|
if need_wildcard:
|
||||||
|
|
|
@ -900,11 +900,6 @@ class BaseModel(object):
|
||||||
for c in new.keys():
|
for c in new.keys():
|
||||||
if new[c].manual:
|
if new[c].manual:
|
||||||
del new[c]
|
del new[c]
|
||||||
# Duplicate float fields because they have a .digits
|
|
||||||
# cache (which must be per-registry, not server-wide).
|
|
||||||
for c in new.keys():
|
|
||||||
if new[c]._type == 'float':
|
|
||||||
new[c] = copy.copy(new[c])
|
|
||||||
if hasattr(new, 'update'):
|
if hasattr(new, 'update'):
|
||||||
new.update(cls.__dict__.get(s, {}))
|
new.update(cls.__dict__.get(s, {}))
|
||||||
elif s=='_constraints':
|
elif s=='_constraints':
|
||||||
|
@ -940,6 +935,13 @@ class BaseModel(object):
|
||||||
if not getattr(cls, '_original_module', None):
|
if not getattr(cls, '_original_module', None):
|
||||||
cls._original_module = cls._module
|
cls._original_module = cls._module
|
||||||
obj = object.__new__(cls)
|
obj = object.__new__(cls)
|
||||||
|
|
||||||
|
if hasattr(obj, '_columns'):
|
||||||
|
# float fields are registry-dependent (digit attribute). Duplicate them to avoid issues.
|
||||||
|
for c, f in obj._columns.items():
|
||||||
|
if f._type == 'float':
|
||||||
|
obj._columns[c] = copy.copy(f)
|
||||||
|
|
||||||
obj.__init__(pool, cr)
|
obj.__init__(pool, cr)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@ -2703,12 +2705,12 @@ class BaseModel(object):
|
||||||
f for f in fields
|
f for f in fields
|
||||||
if f not in ('id', 'sequence')
|
if f not in ('id', 'sequence')
|
||||||
if fget[f]['type'] in ('integer', 'float')
|
if fget[f]['type'] in ('integer', 'float')
|
||||||
if (f in self._columns and getattr(self._columns[f], '_classic_write'))]
|
if (f in self._all_columns and getattr(self._all_columns[f].column, '_classic_write'))]
|
||||||
for f in aggregated_fields:
|
for f in aggregated_fields:
|
||||||
group_operator = fget[f].get('group_operator', 'sum')
|
group_operator = fget[f].get('group_operator', 'sum')
|
||||||
if flist:
|
if flist:
|
||||||
flist += ', '
|
flist += ', '
|
||||||
qualified_field = '"%s"."%s"' % (self._table, f)
|
qualified_field = self._inherits_join_calc(f, query)
|
||||||
flist += "%s(%s) AS %s" % (group_operator, qualified_field, f)
|
flist += "%s(%s) AS %s" % (group_operator, qualified_field, f)
|
||||||
|
|
||||||
gb = groupby and (' GROUP BY ' + qualified_groupby_field) or ''
|
gb = groupby and (' GROUP BY ' + qualified_groupby_field) or ''
|
||||||
|
@ -4253,7 +4255,8 @@ class BaseModel(object):
|
||||||
if not src_trans:
|
if not src_trans:
|
||||||
src_trans = vals[f]
|
src_trans = vals[f]
|
||||||
# Inserting value to DB
|
# Inserting value to DB
|
||||||
self.write(cr, user, ids, {f: vals[f]})
|
context_wo_lang = dict(context, lang=None)
|
||||||
|
self.write(cr, user, ids, {f: vals[f]}, context=context_wo_lang)
|
||||||
self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans)
|
self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -254,9 +254,7 @@ class rml_parse(object):
|
||||||
d = DEFAULT_DIGITS = 2
|
d = DEFAULT_DIGITS = 2
|
||||||
if dp:
|
if dp:
|
||||||
decimal_precision_obj = self.pool['decimal.precision']
|
decimal_precision_obj = self.pool['decimal.precision']
|
||||||
ids = decimal_precision_obj.search(self.cr, self.uid, [('name', '=', dp)])
|
d = decimal_precision_obj.precision_get(self.cr, self.uid, dp)
|
||||||
if ids:
|
|
||||||
d = decimal_precision_obj.browse(self.cr, self.uid, ids)[0].digits
|
|
||||||
elif obj and f:
|
elif obj and f:
|
||||||
res_digits = getattr(obj._columns[f], 'digits', lambda x: ((16, DEFAULT_DIGITS)))
|
res_digits = getattr(obj._columns[f], 'digits', lambda x: ((16, DEFAULT_DIGITS)))
|
||||||
if isinstance(res_digits, tuple):
|
if isinstance(res_digits, tuple):
|
||||||
|
|
|
@ -160,7 +160,7 @@ def exp_get_progress(id):
|
||||||
raise Exception, e
|
raise Exception, e
|
||||||
|
|
||||||
def exp_drop(db_name):
|
def exp_drop(db_name):
|
||||||
if not exp_db_exist(db_name):
|
if db_name not in exp_list(True):
|
||||||
return False
|
return False
|
||||||
openerp.modules.registry.RegistryManager.delete(db_name)
|
openerp.modules.registry.RegistryManager.delete(db_name)
|
||||||
openerp.sql_db.close_db(db_name)
|
openerp.sql_db.close_db(db_name)
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
import unittest2
|
import unittest2
|
||||||
from . import test_mail_examples
|
from . import test_mail_examples
|
||||||
from openerp.tools import html_sanitize, html_email_clean, append_content_to_html, plaintext2html
|
from openerp.tools import html_sanitize, html_email_clean, append_content_to_html, plaintext2html, email_split
|
||||||
|
|
||||||
|
|
||||||
class TestSanitizer(unittest2.TestCase):
|
class TestSanitizer(unittest2.TestCase):
|
||||||
|
@ -325,6 +325,19 @@ class TestHtmlTools(unittest2.TestCase):
|
||||||
for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
|
for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
|
||||||
self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')
|
self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')
|
||||||
|
|
||||||
|
class TestEmailTools(unittest2.TestCase):
|
||||||
|
""" Test some of our generic utility functions for emails """
|
||||||
|
|
||||||
|
def test_email_split(self):
|
||||||
|
cases = [
|
||||||
|
("John <12345@gmail.com>", ['12345@gmail.com']), # regular form
|
||||||
|
("d@x; 1@2", ['d@x', '1@2']), # semi-colon + extra space
|
||||||
|
("'(ss)' <123@gmail.com>, 'foo' <foo@bar>", ['123@gmail.com','foo@bar']), # comma + single-quoting
|
||||||
|
('"john@gmail.com"<johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting
|
||||||
|
('"<jg>" <johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting with brackets
|
||||||
|
]
|
||||||
|
for text, expected in cases:
|
||||||
|
self.assertEqual(email_split(text), expected, 'email_split is broken')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest2.main()
|
unittest2.main()
|
||||||
|
|
|
@ -29,6 +29,7 @@ import re
|
||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from email.utils import getaddresses
|
||||||
|
|
||||||
import openerp
|
import openerp
|
||||||
from openerp.loglevels import ustr
|
from openerp.loglevels import ustr
|
||||||
|
@ -559,4 +560,9 @@ def email_split(text):
|
||||||
""" Return a list of the email addresses found in ``text`` """
|
""" Return a list of the email addresses found in ``text`` """
|
||||||
if not text:
|
if not text:
|
||||||
return []
|
return []
|
||||||
return re.findall(r'([^ ,<@]+@[^> ,]+)', text)
|
return [addr[1] for addr in getaddresses([text])
|
||||||
|
# getaddresses() returns '' when email parsing fails, and
|
||||||
|
# sometimes returns emails without at least '@'. The '@'
|
||||||
|
# is strictly required in RFC2822's `addr-spec`.
|
||||||
|
if addr[1]
|
||||||
|
if '@' in addr[1]]
|
|
@ -172,7 +172,7 @@ class GettextAlias(object):
|
||||||
cr = getattr(s, 'cr', None)
|
cr = getattr(s, 'cr', None)
|
||||||
if not cr and allow_create:
|
if not cr and allow_create:
|
||||||
db = self._get_db()
|
db = self._get_db()
|
||||||
if db:
|
if db is not None:
|
||||||
cr = db.cursor()
|
cr = db.cursor()
|
||||||
is_new_cr = True
|
is_new_cr = True
|
||||||
return cr, is_new_cr
|
return cr, is_new_cr
|
||||||
|
|
Loading…
Reference in New Issue