diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index fe701a85661..687151eebd8 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -61,7 +61,7 @@ - + @@ -95,7 +95,7 @@ - + @@ -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)"/> + domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('state','not in',('close','cancelled'))]"/> @@ -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)"/> + domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('state','not in',('close','cancelled'))]"/> diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index da39839b214..2614f272c8c 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -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']), diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 6adae402e0c..a16a542445d 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -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) diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py index 9b3862508b0..4e211edf68b 100644 --- a/addons/base_vat/base_vat.py +++ b/addons/base_vat/base_vat.py @@ -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 + # Mexican VAT verification, contributed by Vauxoo # and Panos Christeas __check_vat_mx_re = re.compile(r"(?P[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: diff --git a/addons/delivery/delivery.py b/addons/delivery/delivery.py index 4bc3118dd19..c2039e98f50 100644 --- a/addons/delivery/delivery.py +++ b/addons/delivery/delivery.py @@ -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 diff --git a/addons/gamification/views/badge.xml b/addons/gamification/views/badge.xml index 0b37e62051d..ad0aee76b07 100644 --- a/addons/gamification/views/badge.xml +++ b/addons/gamification/views/badge.xml @@ -40,7 +40,6 @@
diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index d38cb705339..4d9e0246234 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -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]) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 5b716a14ef2..5542cd411ec 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -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) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 7c86bb7cc43..d04221cd0f5 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -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) diff --git a/addons/project/report/project_report.py b/addons/project/report/project_report.py index e3f2cd6668b..515973d77b3 100644 --- a/addons/project/report/project_report.py +++ b/addons/project/report/project_report.py @@ -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'), diff --git a/addons/sale/res_config.py b/addons/sale/res_config.py index 94fd74e035e..e81075be03a 100644 --- a/addons/sale/res_config.py +++ b/addons/sale/res_config.py @@ -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): diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 9ac2124c80c..30ada7eab4f 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -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 { diff --git a/addons/sale/views/report_saleorder.xml b/addons/sale/views/report_saleorder.xml index 5c198c635a3..243004869e3 100644 --- a/addons/sale/views/report_saleorder.xml +++ b/addons/sale/views/report_saleorder.xml @@ -45,6 +45,10 @@ Salesperson:

+
+ Validity Date: +

+

Payment Term:

diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 6b8a7c493e1..08b5e133587 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -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 %s created.") % (back_order_name), context=context) + self.message_post(cr, uid, picking.id, body=_("Back order %s created.") % (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): diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py index 638b2aefed4..e21b0bb0223 100644 --- a/addons/survey/controllers/main.py +++ b/addons/survey/controllers/main.py @@ -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']: diff --git a/addons/survey/survey.py b/addons/survey/survey.py index 1389e63e9e2..f110afae352 100644 --- a/addons/survey/survey.py +++ b/addons/survey/survey.py @@ -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}) diff --git a/addons/survey/views/survey_result.xml b/addons/survey/views/survey_result.xml index 40a8a75fc28..c1303b87b0f 100644 --- a/addons/survey/views/survey_result.xml +++ b/addons/survey/views/survey_result.xml @@ -13,9 +13,8 @@

-

-

+
Filters Clear All Filters
@@ -34,6 +33,7 @@

+

@@ -56,7 +56,7 @@ -
+
@@ -115,6 +115,28 @@ + + + + @@ -217,6 +247,13 @@
+ +
+ + + + +
@@ -275,10 +312,7 @@ - -
- +
diff --git a/addons/survey/views/survey_templates.xml b/addons/survey/views/survey_templates.xml index 9ae973b9345..449a6b159cf 100644 --- a/addons/survey/views/survey_templates.xml +++ b/addons/survey/views/survey_templates.xml @@ -8,7 +8,7 @@

Thank you!

-
+
You scored points.
If you wish, you can review your answers.
@@ -70,7 +70,7 @@

-
+
Start Survey @@ -105,7 +105,7 @@