[MERGE] forward port of branch saas-4 up to fa07bc8

This commit is contained in:
Christophe Simonis 2014-07-30 20:30:14 +02:00
commit e4cb5202a0
34 changed files with 253 additions and 87 deletions

View File

@ -61,7 +61,7 @@
<group>
<field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view')]" name="account_id" on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)" groups="account.group_account_user"/>
<field name="invoice_line_tax_id" context="{'type':parent.get('type')}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]" widget="many2many_tags"/>
<field domain="[('type','&lt;&gt;','view'), ('company_id', '=', parent.company_id)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
<field domain="[('type','&lt;&gt;','view'), ('company_id', '=', parent.company_id)], ('state','not in',('close','cancelled'))]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
</group>
</group>
@ -95,7 +95,7 @@
<field name="name"/>
<field name="sequence"/>
<field name="account_id" groups="account.group_account_user"/>
<field name="account_analytic_id" domain="[('type','&lt;&gt;','view'), ('company_id', '=', parent.company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="account_analytic_id" domain="[('type','&lt;&gt;','view'), ('company_id', '=', parent.company_id), ('state','not in',('close','cancelled')]" groups="analytic.group_analytic_accounting"/>
<field name="manual"/>
<field name="amount"/>
<field name="base" readonly="0"/>
@ -201,7 +201,7 @@
domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '=', 'other')]"
on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/>
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"
domain="[('type','!=','view'), ('company_id', '=', parent.company_id)]"/>
domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('state','not in',('close','cancelled'))]"/>
<field name="quantity"/>
<field name="uos_id" groups="product.group_uom"
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/>
@ -358,7 +358,7 @@
domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '=', 'other')]"
on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/>
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"
domain="[('type','!=','view'), ('company_id', '=', parent.company_id)]"/>
domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('state','not in',('close','cancelled'))]"/>
<field name="quantity"/>
<field name="uos_id" groups="product.group_uom"
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/>

View File

@ -587,7 +587,7 @@ class account_move_line(osv.osv):
def _check_no_view(self, cr, uid, ids, context=None):
lines = self.browse(cr, uid, ids, context=context)
for l in lines:
if l.account_id.type == 'view':
if l.account_id.type in ('view', 'consolidation'):
return False
return True
@ -639,7 +639,7 @@ class account_move_line(osv.osv):
return True
_constraints = [
(_check_no_view, 'You cannot create journal items on an account of type view.', ['account_id']),
(_check_no_view, 'You cannot create journal items on an account of type view or consolidation.', ['account_id']),
(_check_no_closed, 'You cannot create journal items on closed account.', ['account_id']),
(_check_company_id, 'Account and Period must belong to the same company.', ['company_id']),
(_check_date, 'The date of your Journal Entry is not in the defined period! You should change the date or remove this constraint from the journal.', ['date']),

View File

@ -941,6 +941,8 @@ class account_voucher(osv.osv):
# refresh to make sure you don't unlink an already removed move
voucher.refresh()
for line in voucher.move_ids:
# refresh to make sure you don't unreconcile an already unreconciled entry
line.refresh()
if line.reconcile_id:
move_lines = [move_line.id for move_line in line.reconcile_id.line_id]
move_lines.remove(line.id)

View File

@ -63,6 +63,7 @@ _ref_vat = {
'mx': 'MXABC123456T1B',
'nl': 'NL123456782B90',
'no': 'NO123456785',
'pe': 'PER10254824220 or PED10254824220',
'pl': 'PL1234567883',
'pt': 'PT123456789',
'ro': 'RO1234567897',
@ -148,10 +149,11 @@ class res_partner(osv.osv):
return cn[0] in string.ascii_lowercase and cn[1] in string.ascii_lowercase
vat_country, vat_number = self._split_vat(self.browse(cr, uid, ids)[0].vat)
vat_no = "'CC##' (CC=Country Code, ##=VAT Number)"
error_partner = self.browse(cr, uid, ids, context=context)
if default_vat_check(vat_country, vat_number):
vat_no = _ref_vat[vat_country] if vat_country in _ref_vat else vat_no
#Retrieve the current partner for wich the VAT is not valid
error_partner = self.browse(cr, uid, ids, context=context)
if self.pool['res.users'].browse(cr, uid, uid).company_id.vat_check_vies:
return '\n' + _('The VAT number [%s] for partner [%s] either failed the VIES VAT validation check or did not respect the expected format %s.') % (error_partner[0].vat, error_partner[0].name, vat_no)
return '\n' + _('The VAT number [%s] for partner [%s] does not seem to be valid. \nNote: the expected format is %s') % (error_partner[0].vat, error_partner[0].name, vat_no)
_constraints = [(check_vat, _construct_constraint_msg, ["vat"])]
@ -222,7 +224,7 @@ class res_partner(osv.osv):
return vat[7] == self._ie_check_char(vat[2:7] + vat[0] + vat[8])
return False
# Mexican VAT verification, contributed by <moylop260@hotmail.com>
# Mexican VAT verification, contributed by Vauxoo
# and Panos Christeas <p_christ@hol.gr>
__check_vat_mx_re = re.compile(r"(?P<primeras>[A-Za-z\xd1\xf1&]{3,4})" \
r"[ \-_]?" \
@ -279,5 +281,39 @@ class res_partner(osv.osv):
return False
return check == int(vat[8])
# Peruvian VAT validation, contributed by Vauxoo
def check_vat_pe(self, vat):
vat_type,vat = vat and len(vat)>=2 and (vat[0], vat[1:]) or (False, False)
if vat_type and vat_type.upper() == 'D':
#DNI
return True
elif vat_type and vat_type.upper() == 'R':
#verify RUC
factor = '5432765432'
sum = 0
dig_check = False
if len(vat) != 11:
return False
try:
int(vat)
except ValueError:
return False
for f in range(0,10):
sum += int(factor[f]) * int(vat[f])
subtraction = 11 - (sum % 11)
if subtraction == 10:
dig_check = 0
elif subtraction == 11:
dig_check = 1
else:
dig_check = subtraction
return int(vat[10]) == dig_check
else:
return False
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -211,7 +211,7 @@ class delivery_grid(osv.osv):
for line in order.order_line:
if not line.product_id or line.is_delivery:
continue
q = product_uom_obj._compute_qty(cr, uid, line.product_uom.id, line.product_uos_qty, line.product_id.uom_id.id)
q = product_uom_obj._compute_qty(cr, uid, line.product_uom.id, line.product_uom_qty, line.product_id.uom_id.id)
weight += (line.product_id.weight or 0.0) * q
volume += (line.product_id.volume or 0.0) * q
quantity += q

View File

@ -40,7 +40,6 @@
<form string="Badge" version="7.0">
<header>
<button string="Grant this Badge" type="action" name="%(action_grant_wizard)d" class="oe_highlight" attrs="{'invisible': [('remaining_sending','=',0)]}" />
<button string="Check Badge" type="object" name="check_automatic" groups="base.group_no_one" />
</header>
<sheet>
<div class="oe_right oe_button_box">

View File

@ -504,7 +504,7 @@ class hr_employee(osv.osv):
if diff > 0:
leave_id = holiday_obj.create(cr, uid, {'name': _('Allocation for %s') % employee.name, 'employee_id': employee.id, 'holiday_status_id': status_id, 'type': 'add', 'holiday_type': 'employee', 'number_of_days_temp': diff}, context=context)
elif diff < 0:
leave_id = holiday_obj.create(cr, uid, {'name': _('Leave Request for %s') % employee.name, 'employee_id': employee.id, 'holiday_status_id': status_id, 'type': 'remove', 'holiday_type': 'employee', 'number_of_days_temp': abs(diff)}, context=context)
raise osv.except_osv(_('Warning!'), _('You cannot reduce validated allocation requests'))
else:
return False
holiday_obj.signal_confirm(cr, uid, [leave_id])

View File

@ -77,7 +77,8 @@ class mail_message(osv.Model):
def default_get(self, cr, uid, fields, context=None):
# protection for `default_type` values leaking from menu action context (e.g. for invoices)
if context and context.get('default_type') and context.get('default_type') not in self._columns['type'].selection:
if context and context.get('default_type') and context.get('default_type') not in [
val[0] for val in self._columns['type'].selection]:
context = dict(context, default_type=None)
return super(mail_message, self).default_get(cr, uid, fields, context=context)

View File

@ -377,7 +377,10 @@ class mail_thread(osv.AbstractModel):
# automatic logging unless asked not to (mainly for various testing purpose)
if not context.get('mail_create_nolog'):
self.message_post(cr, uid, thread_id, body=_('%s created') % (self._description), context=context)
ir_model_pool = self.pool['ir.model']
ids = ir_model_pool.search(cr, uid, [('model', '=', self._name)], context=context)
name = ir_model_pool.read(cr, uid, ids, ['name'], context=context)[0]['name']
self.message_post(cr, uid, thread_id, body=_('%s created') % name, context=context)
# auto_subscribe: take values and defaults into account
create_values = dict(values)

View File

@ -50,7 +50,6 @@ class report_project_task_user(osv.osv):
'nbr': fields.integer('# of tasks', readonly=True),
'priority': fields.selection([('0','Low'), ('1','Normal'), ('2','High')],
string='Priority', readonly=True),
'state': fields.selection([('draft', 'Draft'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')],'Status', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'partner_id': fields.many2one('res.partner', 'Contact', readonly=True),
'stage_id': fields.many2one('project.task.type', 'Stage'),

View File

@ -93,6 +93,7 @@ Example: 10% for retailers, promotion of 5 EUR on this product, etc."""),
product = ir_model_data.xmlid_to_object(cr, uid, 'product.product_product_consultant')
if product and product.exists():
res['time_unit'] = product.uom_id.id
res['timesheet'] = res.get('module_account_analytic_analysis')
return res
def _get_default_time_unit(self, cr, uid, context=None):

View File

@ -283,7 +283,7 @@ class sale_order(osv.osv):
return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
def copy_quotation(self, cr, uid, ids, context=None):
id = self.copy(cr, uid, ids[0], context=None)
id = self.copy(cr, uid, ids[0], context=context)
view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'view_order_form')
view_id = view_ref and view_ref[1] or False,
return {

View File

@ -45,6 +45,10 @@
<strong>Salesperson:</strong>
<p t-field="o.user_id.name"/>
</div>
<div t-if="o.validity_date" class="col-xs-3">
<strong>Validity Date:</strong>
<p t-field="o.validity_date"/>
</div>
<div t-if="o.payment_term" class="col-xs-3">
<strong>Payment Term:</strong>
<p t-field="o.payment_term"/>

View File

@ -932,8 +932,7 @@ class stock_picking(osv.osv):
'pack_operation_ids': [],
'backorder_id': picking.id,
})
back_order_name = self.browse(cr, uid, backorder_id, context=context).name
self.message_post(cr, uid, picking.id, body=_("Back order <em>%s</em> <b>created</b>.") % (back_order_name), context=context)
self.message_post(cr, uid, picking.id, body=_("Back order <em>%s</em> <b>created</b>.") % (picking.name), context=context)
move_obj = self.pool.get("stock.move")
move_obj.write(cr, uid, backorder_move_ids, {'picking_id': backorder_id}, context=context)
@ -1443,7 +1442,7 @@ class stock_production_lot(osv.osv):
'product_id': lambda x, y, z, c: c.get('product_id', False),
}
_sql_constraints = [
('name_ref_uniq', 'unique (name, ref)', 'The combination of Serial Number and internal reference must be unique !'),
('name_ref_uniq', 'unique (name, ref, product_id, company_id)', 'The combination of Serial Number, internal reference, Product and Company must be unique !'),
]
def action_traceability(self, cr, uid, ids, context=None):

View File

@ -298,7 +298,11 @@ class WebsiteSurvey(http.Controller):
type='http', auth='user', website=True)
def survey_reporting(self, survey, token=None, **post):
'''Display survey Results & Statistics for given survey.'''
result_template, current_filters, filter_display_data, filter_finish = 'survey.result', [], [], False
result_template ='survey.result'
current_filters = []
filter_display_data = []
filter_finish = False
survey_obj = request.registry['survey.survey']
if not survey.user_input_ids or not [input_id.id for input_id in survey.user_input_ids if input_id.state != 'new']:
result_template = 'survey.no_result'
@ -307,7 +311,7 @@ class WebsiteSurvey(http.Controller):
filter_finish = True
if post or filter_finish:
filter_data = self.get_filter_data(post)
current_filters = survey_obj.filter_input_ids(request.cr, request.uid, filter_data, filter_finish, context=request.context)
current_filters = survey_obj.filter_input_ids(request.cr, request.uid, survey, filter_data, filter_finish, context=request.context)
filter_display_data = survey_obj.get_filter_display_data(request.cr, request.uid, filter_data, context=request.context)
return request.website.render(result_template,
{'survey': survey,
@ -317,11 +321,48 @@ class WebsiteSurvey(http.Controller):
'filter_display_data': filter_display_data,
'filter_finish': filter_finish
})
# Quick retroengineering of what is injected into the template for now:
# (TODO: flatten and simplify this)
#
# survey: a browse record of the survey
# survey_dict: very messy dict containing all the info to display answers
# {'page_ids': [
#
# ...
#
# {'page': browse record of the page,
# 'question_ids': [
#
# ...
#
# {'graph_data': data to be displayed on the graph
# 'input_summary': number of answered, skipped...
# 'prepare_result': {
# answers displayed in the tables
# }
# 'question': browse record of the question_ids
# }
#
# ...
#
# ]
# }
#
# ...
#
# ]
# }
#
# page_range: pager helper function
# current_filters: a list of ids
# filter_display_data: [{'labels': ['a', 'b'], question_text} ... ]
# filter_finish: boolean => only finished surveys or not
#
def prepare_result_dict(self,survey, current_filters=[]):
"""Returns dictionary having values for rendering template"""
survey_obj = request.registry['survey.survey']
result = {'survey':survey, 'page_ids': []}
result = {'page_ids': []}
for page in survey.page_ids:
page_dict = {'page': page, 'question_ids': []}
for question in page.question_ids:
@ -353,9 +394,10 @@ class WebsiteSurvey(http.Controller):
result = []
if question.type == 'multiple_choice':
result.append({'key': str(question.question),
'values': survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context)})
'values': survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context)['answers']
})
if question.type == 'simple_choice':
result = survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context)
result = survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context)['answers']
if question.type == 'matrix':
data = survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context)
for answer in data['answers']:

View File

@ -282,7 +282,7 @@ class survey_survey(osv.Model):
else:
return (pages[current_page_index + 1][1], current_page_index + 1, False)
def filter_input_ids(self, cr, uid, filters, finished=False, context=None):
def filter_input_ids(self, cr, uid, survey, filters, finished=False, context=None):
'''If user applies any filters, then this function returns list of
filtered user_input_id and label's strings for display data in web.
:param filters: list of dictionary (having: row_id, ansewr_id)
@ -311,7 +311,7 @@ class survey_survey(osv.Model):
if finished:
user_input = self.pool.get('survey.user_input')
if not filtered_input_ids:
current_filters = user_input.search(cr, uid, [], context=context)
current_filters = user_input.search(cr, uid, [('survey_id', '=', survey.id)], context=context)
user_input_objs = user_input.browse(cr, uid, current_filters, context=context)
else:
user_input_objs = user_input.browse(cr, uid, filtered_input_ids, context=context)
@ -345,16 +345,20 @@ class survey_survey(osv.Model):
context = {}
#Calculate and return statistics for choice
if question.type in ['simple_choice', 'multiple_choice']:
result_summary = {}
[result_summary.update({label.id: {'text': label.value, 'count': 0, 'answer_id': label.id}}) for label in question.labels_ids]
answers = {}
comments = []
[answers.update({label.id: {'text': label.value, 'count': 0, 'answer_id': label.id}}) for label in question.labels_ids]
for input_line in question.user_input_line_ids:
if input_line.answer_type == 'suggestion' and result_summary.get(input_line.value_suggested.id) and (not(current_filters) or input_line.user_input_id.id in current_filters):
result_summary[input_line.value_suggested.id]['count'] += 1
result_summary = result_summary.values()
if input_line.answer_type == 'suggestion' and answers.get(input_line.value_suggested.id) and (not(current_filters) or input_line.user_input_id.id in current_filters):
answers[input_line.value_suggested.id]['count'] += 1
if input_line.answer_type == 'text' and (not(current_filters) or input_line.user_input_id.id in current_filters):
comments.append(input_line)
result_summary = {'answers': answers.values(), 'comments': comments}
#Calculate and return statistics for matrix
if question.type == 'matrix':
rows, answers, res = {}, {}, {}
comments = []
[rows.update({label.id: label.value}) for label in question.labels_ids_2]
[answers.update({label.id: label.value}) for label in question.labels_ids]
for cell in product(rows.keys(), answers.keys()):
@ -362,7 +366,9 @@ class survey_survey(osv.Model):
for input_line in question.user_input_line_ids:
if input_line.answer_type == 'suggestion' and (not(current_filters) or input_line.user_input_id.id in current_filters):
res[(input_line.value_suggested_row.id, input_line.value_suggested.id)] += 1
result_summary = {'answers': answers, 'rows': rows, 'result': res}
if input_line.answer_type == 'text' and (not(current_filters) or input_line.user_input_id.id in current_filters):
comments.append(input_line)
result_summary = {'answers': answers, 'rows': rows, 'result': res, 'comments': comments}
#Calculate and return statistics for free_text, textbox, datetime
if question.type in ['free_text', 'textbox', 'datetime']:
@ -1138,11 +1144,14 @@ class survey_user_input_line(osv.Model):
vals.update({'answer_type': 'suggestion', 'value_suggested': post[answer_tag]})
else:
vals.update({'answer_type': None, 'skipped': True})
self.create(cr, uid, vals, context=context)
# '-1' indicates 'comment count as an answer' so do not need to record it
if post.get(answer_tag) and post.get(answer_tag) != '-1':
self.create(cr, uid, vals, context=context)
comment_answer = post.pop(("%s_%s" % (answer_tag, 'comment')), '').strip()
if comment_answer:
vals.update({'answer_type': 'text', 'value_text': comment_answer, 'skipped': False})
vals.update({'answer_type': 'text', 'value_text': comment_answer, 'skipped': False, 'value_suggested': False})
self.create(cr, uid, vals, context=context)
return True
@ -1166,10 +1175,12 @@ class survey_user_input_line(osv.Model):
comment_answer = ca.pop(("%s_%s" % (answer_tag, 'comment')), '').strip()
if len(ca) > 0:
for a in ca:
vals.update({'answer_type': 'suggestion', 'value_suggested': ca[a]})
self.create(cr, uid, vals, context=context)
# '-1' indicates 'comment count as an answer' so do not need to record it
if a != ('%s_%s' % (answer_tag, '-1')):
vals.update({'answer_type': 'suggestion', 'value_suggested': ca[a]})
self.create(cr, uid, vals, context=context)
if comment_answer:
vals.update({'answer_type': 'text', 'value_text': comment_answer})
vals.update({'answer_type': 'text', 'value_text': comment_answer, 'value_suggested': False})
self.create(cr, uid, vals, context=context)
if not ca and not comment_answer:
vals.update({'answer_type': None, 'skipped': True})

View File

@ -13,9 +13,8 @@
<div class="oe_structure" />
<div class="container">
<div class="jumbotron mt32">
<t t-set="survey" t-value="survey_dict['survey']"/>
<h1><span t-field="survey.title"></span> <span style="font-size:1.5em;" class="fa fa-bar-chart-o pull-right "/></h1>
<h2><span t-field="survey.description"></span></h2>
<div t-field="survey.description" class="oe_no_empty" />
</div>
<div class="panel panel-default hidden-print">
<div class="panel-heading"><span class="fa fa-filter"></span> Filters <span t-if="filter_display_data" class="pull-right text-primary clear_survey_filter"><i class="fa fa-times"></i> Clear All Filters</span></div>
@ -34,6 +33,7 @@
<div t-foreach="survey_dict['page_ids']" t-as='page_ids'>
<t t-set="page" t-value="page_ids['page']"/>
<h1 class="mt16" t-field='page.title'></h1>
<div t-field="page.description" class="oe_no_empty" />
<hr/>
<div t-foreach="page_ids['question_ids']" t-as='question_ids' class="mt16">
<t t-set="input_summary" t-value="question_ids['input_summary']"/>
@ -56,7 +56,7 @@
</h4>
<t t-if="input_summary['answered'] != 0">
<t t-if="question.description">
<h5><span class="text-muted" t-field="question.description"></span></h5>
<div class="text-muted oe_no_empty" t-field="question.description" />
</t>
<t t-if="question.type in ['textbox', 'free_text', 'datetime']">
<t t-call="survey.result_text"></t>
@ -115,6 +115,28 @@
<t t-call="survey.pagination" />
</template>
<!-- Result for comments -->
<template id="result_comments" name="Text Result">
<!-- a 'comments' variable must be set an must contain a list of browse records of user input lines -->
<table class="table table-hover table-condensed" t-att-id="'table_question_%d' % question.id">
<thead>
<tr>
<th>#</th>
<th>Comment</th>
</tr>
</thead>
<tbody>
<tr t-foreach="comments" t-as="user_input">
<td><a t-att-href="'%s/%s' % (user_input.user_input_id.print_url, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
<td>
<span t-field="user_input.value_text"></span><br/>
</td>
</tr>
</tbody>
</table>
</template>
<!-- Result for simple_choice and multiple_choice -->
<template id="result_choice" name="Choice Result">
<div>
@ -154,7 +176,7 @@
</tr>
</thead>
<tbody>
<tr t-foreach="prepare_result" t-as="user_input">
<tr t-foreach="prepare_result['answers']" t-as="user_input">
<td>
<p t-esc="user_input['text']"></p>
</td>
@ -168,6 +190,14 @@
</table>
</div>
</div>
<!-- handle comments -->
<div>
<t t-set="comments" t-value="prepare_result['comments']" />
<t t-if="comments">
<t t-call="survey.result_comments" />
</t>
</div>
</div>
</template>
@ -217,6 +247,13 @@
</tbody>
</table>
</div>
<!-- handle comments to matrix -->
<div>
<t t-set="comments" t-value="matrix_result['comments']" />
<t t-if="comments">
<t t-call="survey.result_comments" />
</t>
</div>
</div>
</template>
@ -275,10 +312,7 @@
<tbody>
<tr class="hidden" t-foreach="number_result['input_lines']" t-as="user_input">
<td><a t-att-href="'%s/%s' % (user_input.user_input_id.print_url, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
<!-- <td><t t-esc="user_input_index+1"></t></td>
--> <td>
<span t-field="user_input.value_number"></span><br/>
</td>
<td><span t-field="user_input.value_number"></span><br/></td>
</tr>
</tbody>
</table>

View File

@ -8,7 +8,7 @@
<div class="container">
<div class="jumbotron mt32">
<h1>Thank you!</h1>
<div t-field="survey.thank_you_message" />
<div t-field="survey.thank_you_message" class="oe_no_empty" />
<div t-if='survey.quizz_mode'>You scored <t t-esc="user_input.quizz_score" /> points.</div>
<div>If you wish, you can <a t-att-href="'/survey/print/%s/%s' % (slug(survey), token)">review your answers</a>.</div>
</div>
@ -70,7 +70,7 @@
</div>
<div class='jumbotron mt32'>
<h1 t-field='survey.title' />
<div t-field='survey.description' />
<div t-field='survey.description' class="oe_no_empty" />
<a class="btn btn-primary btn-lg" t-att-href="'/survey/fill/%s/%s' % (slug(survey), token)">
Start Survey
</a>
@ -105,7 +105,7 @@
<div class="page-header">
<p class="pull-right">Page <span t-raw='page_nr + 1'/> on <span t-raw="len(survey.page_ids)"/></p>
<h1 t-field='page.title' />
<div t-field='page.description'/>
<div t-field='page.description' class="oe_no_empty" />
</div>
<form role="form" method="post" class="js_surveyform" t-att-name="'%s_%s' % (survey.id, page.id)" t-att-action="'/survey/fill/%s/%s' % (slug(survey), token)" t-att-data-prefill="'/survey/prefill/%s/%s/%s' % (slug(survey), token, slug(page))" t-att-data-validate="'/survey/validate/%s' % (slug(survey))" t-att-data-submit="'/survey/submit/%s' % (slug(survey))">
@ -118,7 +118,7 @@
<span t-field='question.question' />
<span t-if="question.constr_mandatory" class="text-danger">*</span>
</h2>
<div t-field='question.description' class="text-muted"/>
<div t-field='question.description' class="text-muted oe_no_empty"/>
<t t-if="question.type == 'free_text'"><t t-call="survey.free_text"/></t>
<t t-if="question.type == 'textbox'"><t t-call="survey.textbox"/></t>
<t t-if="question.type == 'numerical_box'"><t t-call="survey.numerical_box"/></t>
@ -268,13 +268,13 @@
<div class="row">
<div class='jumbotron mt32'>
<h1><span t-field='survey.title'/></h1>
<t t-if="survey.description"><div t-field='survey.description'/></t>
<t t-if="survey.description"><div t-field='survey.description' class="oe_no_empty"/></t>
</div>
<form role="form" method="post" class="js_surveyform" t-att-name="'%s' % (survey.id)" t-att-data-prefill="'/survey/prefill/%s/%s' % (slug(survey), token)" t-att-data-scores="'/survey/scores/%s/%s' % (slug(survey), token) if quizz_correction else ''">
<t t-foreach="survey.page_ids" t-as="page">
<div class="page-header">
<h1 t-field='page.title' />
<t t-if="page.description"><div t-field='page.description'/></t>
<t t-if="page.description"><div t-field='page.description' class="oe_no_empty"/></t>
</div>
<t t-foreach='page.question_ids' t-as='question'>
<t t-set="prefix" t-value="'%s_%s_%s' % (survey.id, page.id, question.id)" />
@ -284,7 +284,7 @@
<span t-if="question.constr_mandatory" class="text-danger">*</span>
<span t-if="quizz_correction" class="badge" t-att-data-score-question="question.id"></span>
</h2>
<t t-if="question.description"><div class="text-muted" t-field='question.description' /></t>
<t t-if="question.description"><div class="text-muted oe_no_empty" t-field='question.description' /></t>
<t t-if="question.type == 'free_text'"><t t-call="survey.free_text"/></t>
<t t-if="question.type == 'textbox'"><t t-call="survey.textbox"/></t>
<t t-if="question.type == 'numerical_box'"><t t-call="survey.numerical_box"/></t>

View File

@ -184,12 +184,12 @@ instance.web.Dialog = instance.web.Widget.extend({
close: function(reason) {
if (this.dialog_inited && !this.__tmp_dialog_hiding) {
$('.tooltip').remove(); //remove open tooltip if any to prevent them staying when modal has disappeared
this.trigger("closing", reason);
if (this.$el.is(":data(bs.modal)")) { // may have been destroyed by closing signal
this.__tmp_dialog_hiding = true;
this.$dialog_box.modal('hide');
this.__tmp_dialog_hiding = undefined;
}
this.trigger("closing", reason);
}
},
_closing: function() {

View File

@ -692,8 +692,8 @@ instance.web.ViewManager = instance.web.Widget.extend({
var container = this.$el.find("> .oe_view_manager_body > .oe_view_manager_view_" + view_type);
var view_promise = controller.appendTo(container);
this.views[view_type].controller = controller;
this.views[view_type].deferred.resolve(view_type);
return $.when(view_promise).done(function() {
self.views[view_type].deferred.resolve(view_type);
if (self.searchview
&& self.flags.auto_search
&& view.controller.searchable !== false) {
@ -1120,7 +1120,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
);
}
$.when(defs).done(function() {
$.when(this.views[this.active_view] ? this.views[this.active_view].deferred : $.when(), defs).done(function() {
self.views[self.active_view].controller.do_load_state(state, warm);
});
},

View File

@ -191,14 +191,19 @@ class Website(openerp.addons.web.controllers.main.Home):
modules_to_update = []
for temp_id in templates:
view = request.registry['ir.ui.view'].browse(request.cr, request.uid, int(temp_id), context=request.context)
if view.page:
continue
view.model_data_id.write({
'noupdate': False
})
if view.model_data_id.module not in modules_to_update:
modules_to_update.append(view.model_data_id.module)
module_obj = request.registry['ir.module.module']
module_ids = module_obj.search(request.cr, request.uid, [('name', 'in', modules_to_update)], context=request.context)
module_obj.button_immediate_upgrade(request.cr, request.uid, module_ids, context=request.context)
if modules_to_update:
module_obj = request.registry['ir.module.module']
module_ids = module_obj.search(request.cr, request.uid, [('name', 'in', modules_to_update)], context=request.context)
if module_ids:
module_obj.button_immediate_upgrade(request.cr, request.uid, module_ids, context=request.context)
return request.redirect(redirect)
@http.route('/website/customize_template_get', type='json', auth='user', website=True)

View File

@ -186,7 +186,7 @@ class ir_http(orm.AbstractModel):
if 'qweb_exception' in values:
view = request.registry.get("ir.ui.view")
views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context)
to_reset = [v for v in views if v.model_data_id.noupdate is True]
to_reset = [v for v in views if v.model_data_id.noupdate is True and not v.page]
values['views'] = to_reset
elif code == 403:
logger.warn("403 Forbidden:\n\n%s", values['traceback'])

View File

@ -375,6 +375,13 @@
}
.oe_overlay .oe_overlay_options > .btn-group {
left: -50%;
white-space: nowrap;
}
.oe_overlay .oe_overlay_options > .btn-group > a {
cursor: pointer;
display: inline-block;
float: none;
margin: 0 -3px;
}
.oe_overlay .oe_overlay_options .btn, .oe_overlay .oe_overlay_options a {
cursor: pointer;

View File

@ -279,6 +279,12 @@
z-index: 1002
> .btn-group
left: -50%
white-space: nowrap
> a
cursor: pointer
display: inline-block
float: none
margin: 0 -3px
.btn, a
cursor: pointer
.dropdown

View File

@ -552,7 +552,13 @@
observer.disconnect();
var editor = this.rte.editor;
var root = editor.element && editor.element.$;
editor.destroy();
try {
editor.destroy();
}
catch(err) {
// Hack to avoid the lost of all changes because ckeditor fails in destroy
console.log("Error in editor.destroy() : " + err.toString() + "\n " + err.stack);
}
// FIXME: select editables then filter by dirty?
var defs = this.rte.fetch_editables(root)
.filter('.oe_dirty')

View File

@ -58,7 +58,7 @@ class BlogPost(osv.Model):
'tag_ids': fields.many2many(
'blog.tag', string='Tags',
),
'content': fields.html('Content', translate=True),
'content': fields.html('Content', translate=True, sanitize=False),
# website control
'website_published': fields.boolean(
'Publish', help="Publish on the website"

View File

@ -26,11 +26,20 @@ class contactus(http.Controller):
values.update(kwargs=kwargs.items())
return request.website.render("website.contactus", values)
def create_lead(self, request, values):
def create_lead(self, request, values, kwargs):
""" Allow to be overrided """
return request.registry['crm.lead'].create(request.cr, SUPERUSER_ID, values, request.context)
@http.route(['/crm/contactus'], type='http', auth="public", website=True, multilang=True)
def preRenderThanks(self, request, values, kwargs):
""" Allow to be overrided """
company = request.website.company_id
return {
'google_map_url': self.generate_google_map_url(company.street, company.city, company.zip, company.country_id and company.country_id.name_get()[0][1] or ''),
'_values': values,
'_kwargs': kwargs,
}
@http.route(['/crm/contactus'], type='http', auth="public", website=True)
def contactus(self, **kwargs):
def dict_to_str(title, dictvar):
ret = "\n\n%s" % title
@ -38,7 +47,7 @@ class contactus(http.Controller):
ret += "\n%s" % field
return ret
_TECHNICAL = ['show_info'] # Only use for behavior, don't stock it
_TECHNICAL = ['show_info', 'view_from', 'view_callback'] # Only use for behavior, don't stock it
_BLACKLIST = ['id', 'create_uid', 'create_date', 'write_uid', 'write_date', 'user_id', 'active'] # Allow in description
_REQUIRED = ['name', 'contact_name', 'email_from', 'description'] # Could be improved including required from model
@ -57,12 +66,12 @@ class contactus(http.Controller):
if "name" not in kwargs and values.get("contact_name"): # if kwarg.name is empty, it's an error, we cannot copy the contact_name
values["name"] = values.get("contact_name")
# fields validation : Check that required field from model crm_lead exists
error = set(field for field in _REQUIRED if not kwargs.get(field))
error = set(field for field in _REQUIRED if not values.get(field))
values = dict(values, error=error)
if error:
values.update(kwargs=kwargs.items())
return request.website.render("website.contactus", values)
return request.website.render(kwargs.get("view_from", "website.contactus"), values)
try:
values['channel_id'] = request.registry['ir.model.data'].get_object_reference(request.cr, SUPERUSER_ID, 'crm', 'crm_case_channel_website')[1]
@ -83,7 +92,7 @@ class contactus(http.Controller):
post_description.append("%s: %s" % ("REFERER", environ.get("HTTP_REFERER")))
values['description'] += dict_to_str(_("Environ Fields: "), post_description)
lead_id = self.create_lead(request, dict(values, user_id=False))
lead_id = self.create_lead(request, dict(values, user_id=False), kwargs)
if lead_id:
for field_value in post_file:
attachment_value = {
@ -96,8 +105,5 @@ class contactus(http.Controller):
}
request.registry['ir.attachment'].create(request.cr, SUPERUSER_ID, attachment_value, context=request.context)
company = request.website.company_id
values = {
'google_map_url': self.generate_google_map_url(company.street, company.city, company.zip, company.country_id and company.country_id.name_get()[0][1] or ''),
}
return request.website.render("website_crm.contactus_thanks", values)
values = self.preRenderThanks(request, values, kwargs)
return request.website.render(kwargs.get("view_callback", "website_crm.contactus_thanks"), values)

View File

@ -117,7 +117,7 @@
<xpath expr="//ul[@id='reseller_countries']" position="after">
<h3>World Map</h3>
<ul class="nav">
<iframe t-attf-src="/google_map/?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners"
<iframe t-attf-src="/google_map/?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners/"
style="width:260px; height:260px; border:0; padding:0; margin:0;"></iframe>
</ul>
</xpath>

View File

@ -48,7 +48,7 @@ class WebsiteMembership(http.Controller):
('partner.website_description', 'ilike', post_name)]
# group by country, based on all customers (base domain)
membership_line_ids = membership_line_obj.search(cr, uid, base_line_domain, context=context)
membership_line_ids = membership_line_obj.search(cr, SUPERUSER_ID, base_line_domain, context=context)
countries = partner_obj.read_group(
cr, uid, [('member_lines', 'in', membership_line_ids), ("website_published", "=", True)], ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
@ -70,8 +70,14 @@ class WebsiteMembership(http.Controller):
'country_id': (0, _("All Countries"))
})
# format domain for group_by and memberships
membership_ids = product_obj.search(cr, uid, [('membership', '=', True)], order="website_sequence", context=context)
memberships = product_obj.browse(cr, uid, membership_ids, context=context)
# make sure we don't access to lines with unpublished membershipts
line_domain.append(('membership_id', 'in', membership_ids))
# displayed membership lines
membership_line_ids = membership_line_obj.search(cr, uid, line_domain, context=context)
membership_line_ids = membership_line_obj.search(cr, SUPERUSER_ID, line_domain, context=context)
membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
membership_lines.sort(key=lambda x: x.membership_id.website_sequence)
partner_ids = [m.partner.id for m in membership_lines]
@ -79,10 +85,6 @@ class WebsiteMembership(http.Controller):
partners = dict((p.id, p) for p in partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context))
# format domain for group_by and memberships
membership_ids = product_obj.search(cr, uid, [('membership', '=', True)], order="website_sequence", context=context)
memberships = product_obj.browse(cr, uid, membership_ids, context=context)
# request pager for lines
pager = request.website.pager(url="/members", total=len(membership_line_ids), page=page, step=self._references_per_page, scope=7, url_args=post)

View File

@ -106,7 +106,7 @@
<ul class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>World Map</h3></li>
<ul class="nav">
<iframe t-attf-src="/google_map/?width=320&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/members"
<iframe t-attf-src="/google_map/?width=320&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/members/"
style="width:320px; height:260px; border:0; padding:0; margin:0;"></iframe>
</ul>
</ul>

View File

@ -12,7 +12,8 @@ import pytz
from openerp.osv import orm
from openerp.tools.translate import _
from openerp.tools.misc import DEFAULT_SERVER_DATE_FORMAT,\
DEFAULT_SERVER_DATETIME_FORMAT
DEFAULT_SERVER_DATETIME_FORMAT,\
ustr
from openerp.tools import html_sanitize
REFERENCING_FIELDS = set([None, 'id', '.id'])
@ -260,6 +261,7 @@ class ir_fields_converter(orm.Model):
# Or just copy context & remove lang?
selection = selection(model, cr, uid, context=None)
for item, label in selection:
label = ustr(label)
labels = self._get_translations(
cr, uid, ('selection', 'model', 'code'), label, context=context)
labels.append(label)
@ -268,8 +270,8 @@ class ir_fields_converter(orm.Model):
raise ValueError(
_(u"Value '%s' not found in selection field '%%(field)s'") % (
value), {
'moreinfo': [label or unicode(item) for item, label in selection
if label or item]
'moreinfo': [_label or unicode(item) for item, _label in selection
if _label or item]
})

View File

@ -303,10 +303,10 @@ class ir_values(osv.osv):
(SELECT company_id from res_users where id = %%s)
)
%s
ORDER BY v.user_id, u.company_id, v.key2"""
ORDER BY v.user_id, u.company_id"""
params = ('default', model, uid, uid)
if condition:
query %= 'AND (v.key2 = %s OR v.key2 IS NULL)'
query %= 'AND v.key2 = %s'
params += (condition[:200],)
else:
query %= 'AND v.key2 is NULL'

View File

@ -72,7 +72,7 @@ _SAFE_OPCODES = _EXPR_OPCODES.union(set(opmap[x] for x in [
# New in Python 2.7 - http://bugs.python.org/issue4715 :
'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE',
'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY',
'LOAD_FAST', 'STORE_FAST', 'DELETE_FAST',
'LOAD_FAST', 'STORE_FAST', 'DELETE_FAST', 'UNPACK_SEQUENCE',
'LOAD_GLOBAL', # Only allows access to restricted globals
] if x in opmap))
@ -300,6 +300,7 @@ def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=Fal
'range': range,
'xrange': xrange,
'zip': zip,
'Exception': Exception,
}
)
if locals_builtins:

View File

@ -788,7 +788,7 @@ def trans_generate(lang, modules, cr):
for bin_path in ['osv', 'report' ]:
path_list.append(os.path.join(config.config['root_path'], bin_path))
_logger.debug("Scanning modules at paths: ", path_list)
_logger.debug("Scanning modules at paths: %s", path_list)
mod_paths = list(path_list)