diff --git a/addons/account/account.py b/addons/account/account.py index 33b52b643ed..82957fc93d3 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -137,16 +137,27 @@ class account_account_type(osv.osv): _name = "account.account.type" _description = "Account Type" - def _get_current_report_type(self, cr, uid, ids, name, arg, context=None): + def _get_financial_report_ref(self, cr, uid, context=None): obj_data = self.pool.get('ir.model.data') obj_financial_report = self.pool.get('account.financial.report') + financial_report_ref = {} + for key, financial_report in [ + ('asset','account_financial_report_assets0'), + ('liability','account_financial_report_liability0'), + ('income','account_financial_report_income0'), + ('expense','account_financial_report_expense0'), + ]: + try: + financial_report_ref[key] = obj_financial_report.browse(cr, uid, + obj_data.get_object_reference(cr, uid, 'account', financial_report)[1], + context=context) + except ValueError: + pass + return financial_report_ref + + def _get_current_report_type(self, cr, uid, ids, name, arg, context=None): res = {} - financial_report_ref = { - 'asset': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_assets0')[1], context=context), - 'liability': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_liability0')[1], context=context), - 'income': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_income0')[1], context=context), - 'expense': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_expense0')[1], context=context), - } + financial_report_ref = self._get_financial_report_ref(cr, uid, context=context) for record in self.browse(cr, uid, ids, context=context): res[record.id] = 'none' for key, financial_report in financial_report_ref.items(): @@ -157,15 +168,9 @@ class account_account_type(osv.osv): def _save_report_type(self, cr, uid, account_type_id, field_name, field_value, arg, context=None): field_value = field_value or 'none' - obj_data = self.pool.get('ir.model.data') obj_financial_report = self.pool.get('account.financial.report') #unlink if it exists somewhere in the financial reports related to BS or PL - financial_report_ref = { - 'asset': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_assets0')[1], context=context), - 'liability': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_liability0')[1], context=context), - 'income': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_income0')[1], context=context), - 'expense': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_expense0')[1], context=context), - } + financial_report_ref = self._get_financial_report_ref(cr, uid, context=context) for key, financial_report in financial_report_ref.items(): list_ids = [x.id for x in financial_report.account_type_ids] if account_type_id in list_ids: @@ -1258,6 +1263,10 @@ class account_move(osv.osv): return [('id', 'in', tuple(ids))] return [('id', '=', '0')] + def _get_move_from_lines(self, cr, uid, ids, context=None): + line_obj = self.pool.get('account.move.line') + return [line.move_id.id for line in line_obj.browse(cr, uid, ids, context=context)] + _columns = { 'name': fields.char('Number', size=64, required=True), 'ref': fields.char('Reference', size=64), @@ -1267,7 +1276,10 @@ class account_move(osv.osv): help='All manually created new journal entries are usually in the status \'Unposted\', but you can set the option to skip that status on the related journal. In that case, they will behave as journal entries automatically created by the system on document validation (invoices, bank statements...) and will be created in \'Posted\' status.'), 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}), 'to_check': fields.boolean('To Review', help='Check this box if you are unsure of that journal entry and if you want to note it as \'to be reviewed\' by an accounting expert.'), - 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True), + 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store={ + _name: (lambda self, cr,uid,ids,c: ids, ['line_id'], 10), + 'account.move.line': (_get_move_from_lines, ['partner_id'],10) + }), 'amount': fields.function(_amount_compute, string='Amount', digits_compute=dp.get_precision('Account'), type='float', fnct_search=_search_amount), 'date': fields.date('Date', required=True, states={'posted':[('readonly',True)]}, select=True), 'narration':fields.text('Internal Note'), @@ -1633,9 +1645,11 @@ class account_move(osv.osv): else: # We can't validate it (it's unbalanced) # Setting the lines as draft - obj_move_line.write(cr, uid, line_ids, { - 'state': 'draft' - }, context, check=False) + not_draft_line_ids = list(set(line_ids) - set(line_draft_ids)) + if not_draft_line_ids: + obj_move_line.write(cr, uid, not_draft_line_ids, { + 'state': 'draft' + }, context, check=False) # Create analytic lines for the valid moves for record in valid_moves: obj_move_line.create_analytic_lines(cr, uid, [line.id for line in record.line_id], context) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index bf1da5cb71b..42f3effd6f9 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -1431,6 +1431,7 @@ class account_invoice_line(osv.osv): _name = "account.invoice.line" _description = "Invoice Line" + _order = "invoice_id,sequence,id" _columns = { 'name': fields.text('Description', required=True), 'origin': fields.char('Source Document', size=256, help="Reference of the document that produced this invoice."), @@ -1467,6 +1468,7 @@ class account_invoice_line(osv.osv): 'discount': 0.0, 'price_unit': _price_unit_default, 'account_id': _default_account_id, + 'sequence': 10, } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 5161fb37fec..d56fe27759c 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -192,6 +192,7 @@ + diff --git a/addons/account/project/wizard/account_analytic_chart_view.xml b/addons/account/project/wizard/account_analytic_chart_view.xml index 70769049002..50a13acef18 100644 --- a/addons/account/project/wizard/account_analytic_chart_view.xml +++ b/addons/account/project/wizard/account_analytic_chart_view.xml @@ -7,16 +7,16 @@ account.analytic.chart
-
-
+
+
diff --git a/addons/account/report/account_financial_report.py b/addons/account/report/account_financial_report.py index 864c4bbc7d1..2b1f5af4d68 100644 --- a/addons/account/report/account_financial_report.py +++ b/addons/account/report/account_financial_report.py @@ -38,6 +38,7 @@ class report_account_common(report_sxw.rml_parse, common_report_header): 'get_filter': self._get_filter, 'get_start_date':self._get_start_date, 'get_end_date':self._get_end_date, + 'get_target_move': self._get_target_move, }) self.context = context diff --git a/addons/account/report/account_financial_report.rml b/addons/account/report/account_financial_report.rml index 1ee8d5b1858..7949077e43f 100644 --- a/addons/account/report/account_financial_report.rml +++ b/addons/account/report/account_financial_report.rml @@ -166,11 +166,12 @@ - + Chart of Accounts Fiscal Year Filter By [[ get_filter(data)!='No Filters' and get_filter(data) ]] + Target Moves [[ get_account(data) or removeParentNode('para') ]] @@ -197,6 +198,10 @@ + + [[ get_target_move(data) ]] + + diff --git a/addons/account/wizard/account_financial_report.py b/addons/account/wizard/account_financial_report.py index 2ce7118335a..48bdfd33081 100644 --- a/addons/account/wizard/account_financial_report.py +++ b/addons/account/wizard/account_financial_report.py @@ -54,7 +54,7 @@ class accounting_report(osv.osv_memory): 'target_move': 'posted', 'account_report_id': _get_account_report, } - + def _build_comparison_context(self, cr, uid, ids, data, context=None): if context is None: context = {} @@ -62,6 +62,7 @@ class accounting_report(osv.osv_memory): result['fiscalyear'] = 'fiscalyear_id_cmp' in data['form'] and data['form']['fiscalyear_id_cmp'] or False result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False result['chart_account_id'] = 'chart_account_id' in data['form'] and data['form']['chart_account_id'] or False + result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or '' if data['form']['filter_cmp'] == 'filter_date': result['date_from'] = data['form']['date_from_cmp'] result['date_to'] = data['form']['date_to_cmp'] @@ -86,7 +87,7 @@ class accounting_report(osv.osv_memory): return res def _print_report(self, cr, uid, ids, data, context=None): - data['form'].update(self.read(cr, uid, ids, ['date_from_cmp', 'debit_credit', 'date_to_cmp', 'fiscalyear_id_cmp', 'period_from_cmp', 'period_to_cmp', 'filter_cmp', 'account_report_id', 'enable_filter', 'label_filter'], context=context)[0]) + data['form'].update(self.read(cr, uid, ids, ['date_from_cmp', 'debit_credit', 'date_to_cmp', 'fiscalyear_id_cmp', 'period_from_cmp', 'period_to_cmp', 'filter_cmp', 'account_report_id', 'enable_filter', 'label_filter','target_move'], context=context)[0]) return { 'type': 'ir.actions.report.xml', 'report_name': 'account.financial.report', diff --git a/addons/account/wizard/account_invoice_refund.py b/addons/account/wizard/account_invoice_refund.py index 5be9ebc5c80..4b7a7fd5314 100644 --- a/addons/account/wizard/account_invoice_refund.py +++ b/addons/account/wizard/account_invoice_refund.py @@ -157,8 +157,8 @@ class account_invoice_refund(osv.osv_memory): for line in movelines: if line.account_id.id == inv.account_id.id: to_reconcile_ids[line.account_id.id] = [line.id] - if type(line.reconcile_id) != osv.orm.browse_null: - reconcile_obj.unlink(cr, uid, line.reconcile_id.id) + if line.reconcile_id: + line.reconcile_id.unlink() inv_obj.signal_invoice_open(cr, uid, [refund.id]) refund = inv_obj.browse(cr, uid, refund_id[0], context=context) for tmpline in refund.move_id.line_id: diff --git a/addons/account/wizard/account_report_common.py b/addons/account/wizard/account_report_common.py index b8dc562f3a0..6641c280765 100644 --- a/addons/account/wizard/account_report_common.py +++ b/addons/account/wizard/account_report_common.py @@ -154,6 +154,7 @@ class account_common_report(osv.osv_memory): result['fiscalyear'] = 'fiscalyear_id' in data['form'] and data['form']['fiscalyear_id'] or False result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False result['chart_account_id'] = 'chart_account_id' in data['form'] and data['form']['chart_account_id'] or False + result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or '' if data['form']['filter'] == 'filter_date': result['date_from'] = data['form']['date_from'] result['date_to'] = data['form']['date_to'] diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index df852181709..14c8f712e81 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -295,10 +295,6 @@ class account_analytic_account(osv.osv): args=[] if context is None: context={} - if context.get('current_model') == 'project.project': - project_obj = self.pool.get("account.analytic.account") - project_ids = project_obj.search(cr, uid, args) - return self.name_get(cr, uid, project_ids, context=context) if name: account_ids = self.search(cr, uid, [('code', '=', name)] + args, limit=limit, context=context) if not account_ids: diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 64b6f01d05c..d4cd1e083b4 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -578,8 +578,7 @@ property or property parameter."), def do_accept(self, cr, uid, ids, context=None, *args): """ - Update state of invitation as Accepted and if the invited user is other - then event user it will make a copy of this event for invited user. + Marks event invitation as Accepted. @param cr: the current row, from the database cursor @param uid: the current user's ID for security checks @param ids: list of calendar attendee's IDs @@ -589,15 +588,7 @@ property or property parameter."), if context is None: context = {} - for vals in self.browse(cr, uid, ids, context=context): - if vals.ref and vals.ref.user_id: - mod_obj = self.pool[vals.ref._name] - res=mod_obj.read(cr,uid,[vals.ref.id],['duration','class'],context) - defaults = {'user_id': vals.user_id.id, 'organizer_id': vals.ref.user_id.id,'duration':res[0]['duration'],'class':res[0]['class']} - mod_obj.copy(cr, uid, vals.ref.id, default=defaults, context=context) - self.write(cr, uid, vals.id, {'state': 'accepted'}, context) - - return True + return self.write(cr, uid, ids, {'state': 'accepted'}, context) def do_decline(self, cr, uid, ids, context=None, *args): """ @@ -1126,12 +1117,14 @@ rule or repeating pattern of time to exclude from the recurring rule."), for partner in event.partner_ids: if partner.id in attendees: continue + local_context = context.copy() + local_context.pop('default_state', None) att_id = self.pool.get('calendar.attendee').create(cr, uid, { 'partner_id': partner.id, 'user_id': partner.user_ids and partner.user_ids[0].id or False, 'ref': self._name+','+str(event.id), 'email': partner.email - }, context=context) + }, context=local_context) if partner.email: mail_to = mail_to + " " + partner.email self.write(cr, uid, [event.id], { diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 04a17b9dbc0..932fa998d0a 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -160,7 +160,7 @@ class crm_case_section(osv.osv): 'note': fields.text('Description'), 'working_hours': fields.float('Working Hours', digits=(16, 2)), 'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, help="The email address associated with this team. New emails received will automatically " "create new leads assigned to the team."), 'color': fields.integer('Color Index'), diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index c4e2cbdc91d..e49ae818cae 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -634,7 +634,7 @@ class crm_lead(format_address, osv.osv): # Merge notifications about loss of information opportunities = [highest] opportunities.extend(opportunities_rest) - self._merge_notify(cr, uid, highest, opportunities, context=context) + self._merge_notify(cr, uid, highest.id, opportunities, context=context) # Check if the stage is in the stages of the sales team. If not, assign the stage with the lowest sequence if merged_data.get('section_id'): section_stage_ids = self.pool.get('crm.case.stage').search(cr, uid, [('section_ids', 'in', merged_data['section_id']), ('type', '=', merged_data.get('type'))], order='sequence', context=context) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 2e3405ed079..5f264cbbc7c 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -21,6 +21,8 @@ ############################################################################## import base64 +import datetime +import dateutil.relativedelta as relativedelta import logging import openerp @@ -57,6 +59,12 @@ try: 'str': str, 'quote': quote, 'urlencode': urlencode, + 'datetime': datetime, + + # dateutil.relativedelta is an old-style class and cannot be directly + # instanciated wihtin a jinja2 expression, so a lambda "proxy" is + # is needed, apparently. + 'relativedelta': lambda *a, **kw : relativedelta.relativedelta(*a, **kw), }) except ImportError: _logger.warning("jinja2 not available, templating features will not work!") diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index 5d5be0eec97..e25b5ac0894 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -36,13 +36,6 @@ def _employee_get(obj, cr, uid, context=None): class hr_expense_expense(osv.osv): - def copy(self, cr, uid, id, default=None, context=None): - if context is None: - context = {} - if not default: default = {} - default.update({'date_confirm': False, 'date_valid': False, 'user_valid': False}) - return super(hr_expense_expense, self).copy(cr, uid, id, default, context=context) - def _amount(self, cr, uid, ids, field_name, arg, context=None): res= {} for expense in self.browse(cr, uid, ids, context=context): @@ -116,7 +109,11 @@ class hr_expense_expense(osv.osv): def copy(self, cr, uid, id, default=None, context=None): if default is None: default = {} - default.update(account_move_id=False) + default.update( + account_move_id=False, + date_confirm=False, + date_valid=False, + user_valid=False) return super(hr_expense_expense, self).copy(cr, uid, id, default=default, context=context) def unlink(self, cr, uid, ids, context=None): diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 0492dbfbcd4..8664a2c1166 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -463,7 +463,7 @@ class hr_job(osv.osv): _inherits = {'mail.alias': 'alias_id'} _columns = { 'survey_id': fields.many2one('survey', 'Interview Form', help="Choose an interview form for this job position and you will be able to print/answer this interview from all applicants who apply for this job"), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, help="Email alias for this job position. New emails will automatically " "create new applicants for this job position."), } diff --git a/addons/l10n_be/wizard/l10n_be_vat_intra.py b/addons/l10n_be/wizard/l10n_be_vat_intra.py index 56ed91e13fb..1a2d9201790 100644 --- a/addons/l10n_be/wizard/l10n_be_vat_intra.py +++ b/addons/l10n_be/wizard/l10n_be_vat_intra.py @@ -247,9 +247,9 @@ class partner_vat_intra(osv.osv_memory): for client in xml_data['clientlist']: if not client['vatnum']: raise osv.except_osv(_('Insufficient Data!'),_('No vat number defined for %s.') % client['partner_name']) - data_clientinfo +='\n\t\t\n\t\t\t%(vatnum)s\n\t\t\t%(code)s\n\t\t\t%(amount)s\n\t\t' % (client) + data_clientinfo +='\n\t\t\n\t\t\t%(vatnum)s\n\t\t\t%(code)s\n\t\t\t%(amount).2f\n\t\t' % (client) - data_decl = '\n\t' % (xml_data) + data_decl = '\n\t' % (xml_data) data_file += data_head + data_decl + data_comp_period + data_clientinfo + '\n\t\t%(comments)s\n\t\n' % (xml_data) context['file_save'] = data_file diff --git a/addons/l10n_be_coda/wizard/account_coda_import.py b/addons/l10n_be_coda/wizard/account_coda_import.py index aed99345fba..aa9bf49b5a1 100644 --- a/addons/l10n_be_coda/wizard/account_coda_import.py +++ b/addons/l10n_be_coda/wizard/account_coda_import.py @@ -241,11 +241,11 @@ class account_coda_import(osv.osv_memory): if statement['debit'] == '1': # 1=Debit statement['balance_end_real'] = - statement['balance_end_real'] if statement['balance_end_realDate']: - period_id = self.pool.get('account.period').search(cr, uid, [('date_start', '<=', statement['balance_end_realDate']), ('date_stop', '>=', statement['balance_end_realDate'])]) + period_id = self.pool.get('account.period').search(cr, uid, [('company_id', '=', statement['journal_id'].company_id.id), ('date_start', '<=', statement['balance_end_realDate']), ('date_stop', '>=', statement['balance_end_realDate'])]) else: - period_id = self.pool.get('account.period').search(cr, uid, [('date_start', '<=', statement['date']), ('date_stop', '>=', statement['date'])]) + period_id = self.pool.get('account.period').search(cr, uid, [('company_id', '=', statement['journal_id'].company_id.id), ('date_start', '<=', statement['date']), ('date_stop', '>=', statement['date'])]) if not period_id and len(period_id) == 0: - raise osv.except_osv(_('Error') + 'R0002', _("The CODA Statement New Balance date doesn't fall within a defined Accounting Period! Please create the Accounting Period for date %s.") % statement['balance_end_realDate']) + raise osv.except_osv(_('Error') + 'R0002', _("The CODA Statement New Balance date doesn't fall within a defined Accounting Period! Please create the Accounting Period for date %s for the company %s.") % (statement['balance_end_realDate'], statement['journal_id'].company_id.name)) statement['period_id'] = period_id[0] elif line[0] == '9': statement['balanceMin'] = float(rmspaces(line[22:37])) / 1000 diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 9e363065481..5a9f8751f2d 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -76,7 +76,7 @@ class mail_group(osv.Model): help="Small-sized photo of the group. It is automatically "\ "resized as a 64x64px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required."), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, help="The email address associated with this group. New emails received will automatically " "create new topics."), } diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 7bc39c438d1..ac1d406c29a 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -36,7 +36,7 @@ class res_users(osv.Model): _inherits = {'mail.alias': 'alias_id'} _columns = { - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, help="Email address internally associated with this user. Incoming "\ "emails will appear in the user's notifications."), 'display_groups_suggestions': fields.boolean("Display Groups Suggestions"), diff --git a/addons/membership/membership_view.xml b/addons/membership/membership_view.xml index 21fcd3bcd95..3964b8b07b9 100644 --- a/addons/membership/membership_view.xml +++ b/addons/membership/membership_view.xml @@ -158,6 +158,7 @@ res.partner.select res.partner + 50 diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 82db2ace570..b9187956c81 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -318,9 +318,10 @@ class mrp_bom(osv.osv): """ routing_obj = self.pool.get('mrp.routing') factor = factor / (bom.product_efficiency or 1.0) - factor = rounding(factor, bom.product_rounding) - if factor < bom.product_rounding: - factor = bom.product_rounding + max_rounding = max(bom.product_rounding, bom.product_uom.rounding) + factor = rounding(factor, max_rounding) + if factor < max_rounding: + factor = max_rounding result = [] result2 = [] phantom = False diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index f9d489accce..2576235586f 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -1027,7 +1027,7 @@ Bill of Materials [('bom_id','=',False)] mrp.bom - tree + form {'search_default_product_id': [active_id]} diff --git a/addons/mrp/tests/test_multicompany.py b/addons/mrp/tests/test_multicompany.py index 78986bd41b2..f38a31a70de 100644 --- a/addons/mrp/tests/test_multicompany.py +++ b/addons/mrp/tests/test_multicompany.py @@ -33,7 +33,13 @@ class TestMrpMulticompany(common.TransactionCase): self.res_users = self.registry('res.users') self.stock_location = self.registry('stock.location') - model, self.multicompany_user_id = self.ir_model_data.get_object_reference(cr, uid, 'stock', 'multicompany_user') + model, group_user_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') + model, group_stock_manager_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'stock', 'group_stock_manager') + model, company_2_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'stock', 'res_company_2') + self.multicompany_user_id = self.res_users.create(cr, uid, + {'name': 'multicomp', 'login': 'multicomp', + 'groups_id': [(6, 0, [group_user_id, group_stock_manager_id])], + 'company_id': company_2_id, 'company_ids': [(6,0,[company_2_id])]}) def test_00_multicompany_user(self): diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 1084a220038..f200d636268 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -478,6 +478,11 @@ class pos_session(osv.osv): context = {} if not ids: return {} + for session in self.browse(cr, uid, ids, context=context): + if session.user_id.id != uid: + raise osv.except_osv( + _('Error!'), + _("You cannot use the session of another users. This session is owned by %s. Please first close this one to use this point of sale." % session.user_id.name)) context.update({'active_id': ids[0]}) return { 'type' : 'ir.actions.client', @@ -507,7 +512,6 @@ class pos_order(osv.osv): 'pos_reference':order['name'], 'partner_id': order['partner_id'] or False }, context) - for payments in order['statement_ids']: payment = payments[2] self.add_payment(cr, uid, order_id, { @@ -809,9 +813,18 @@ class pos_order(osv.osv): """Create a copy of order for refund order""" clone_list = [] line_obj = self.pool.get('pos.order.line') + for order in self.browse(cr, uid, ids, context=context): + current_session_ids = self.pool.get('pos.session').search(cr, uid, [ + ('state', '!=', 'closed'), + ('user_id', '=', uid)], context=context) + if not current_session_ids: + raise osv.except_osv(_('Error!'), _('To return product(s), you need to open a session that will be used to register the refund.')) + clone_id = self.copy(cr, uid, order.id, { - 'name': order.name + ' REFUND', + 'name': order.name + ' REFUND', # not used, name forced by create + 'session_id': current_session_ids[0], + 'date_order': time.strftime('%Y-%m-%d %H:%M:%S'), }, context=context) clone_list.append(clone_id) diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index 9a74eec0c05..300bc46b093 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -26,7 +26,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal this.custom_payment_status = this.default_payment_status; this.connection = new instance.web.Session(undefined,url); - + this.connection.session_id = _.uniqueId('posproxy'); this.bypass_proxy = false; this.notifications = {}; @@ -439,7 +439,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal // The barcode readers acts as a keyboard, we catch all keyup events and try to find a // barcode sequence in the typed keys, then act accordingly. - $('body').delegate('','keyup', function (e){ + $('body').delegate('','keypress', function (e){ //console.log('keyup:'+String.fromCharCode(e.keyCode)+' '+e.keyCode,e); //We only care about numbers if (e.keyCode >= 48 && e.keyCode < 58){ diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 649b721eb46..63fc84c2ccf 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -93,6 +93,7 @@ class product_pricelist(osv.osv): _name = "product.pricelist" _description = "Pricelist" + _order = 'name' _columns = { 'name': fields.char('Pricelist Name',size=64, required=True, translate=True), 'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the pricelist without removing it."), @@ -111,6 +112,27 @@ class product_pricelist(osv.osv): result.append((pl.id,name)) return result + def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100): + if name and operator == '=' and not args: + # search on the name of the pricelist and its currency, opposite of name_get(), + # Used by the magic context filter in the product search view. + query_args = {'name': name, 'limit': limit} + query = """SELECT p.id + FROM product_pricelist p JOIN + res_currency c ON (p.currency_id = c.id) + WHERE p.name || ' (' || c.name || ')' = %(name)s + ORDER BY p.name""" + if limit: + query += " LIMIT %(limit)s" + cr.execute(query, query_args) + ids = [r[0] for r in cr.fetchall()] + # regular search() to apply ACLs - may limit results below limit in some cases + ids = self.search(cr, uid, [('id', 'in', ids)], limit=limit, context=context) + if ids: + return self.name_get(cr, uid, ids, context) + return super(product_pricelist, self).name_search( + cr, uid, name, args, operator=operator, context=context, limit=limit) + def _get_currency(self, cr, uid, ctx): comp = self.pool.get('res.users').browse(cr, uid, uid).company_id diff --git a/addons/product/product.py b/addons/product/product.py index 6e1128985aa..71f1eeffaf3 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -420,6 +420,11 @@ class product_product(osv.osv): pricelist = context.get('pricelist', False) partner = context.get('partner', False) if pricelist: + # Support context pricelists specified as display_name or ID for compatibility + if isinstance(pricelist, basestring): + pricelist_ids = self.pool.get('product.pricelist').name_search( + cr, uid, pricelist, operator='=', context=context, limit=1) + pricelist = pricelist_ids[0][0] if pricelist_ids else pricelist for id in ids: try: price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], id, quantity, partner=partner, context=context)[pricelist] @@ -762,7 +767,7 @@ class product_product(osv.osv): context = {} if context and context.get('search_default_categ_id', False): args.append((('categ_id', 'child_of', context['search_default_categ_id']))) - return super(product_product, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=False) + return super(product_product, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count) class product_packaging(osv.osv): diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index 3d5161f8595..0256b9f3d53 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -15,7 +15,7 @@ - + diff --git a/addons/project/project.py b/addons/project/project.py index 139d007d3ab..d153a849dc7 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -265,7 +265,7 @@ class project(osv.osv): 'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}), 'task_count': fields.function(_task_count, type='integer', string="Open Tasks"), 'color': fields.integer('Color Index'), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, help="Internal email associated with this project. Incoming emails are automatically synchronized" "with Tasks (or optionally Issues if the Issue Tracker module is installed)."), 'alias_model': fields.selection(_alias_models, "Alias Model", select=True, required=True, @@ -717,6 +717,16 @@ class task(osv.osv): new_name = _("%s (copy)") % (default.get('name', '')) default.update({'name':new_name}) return super(task, self).copy_data(cr, uid, id, default, context) + + def copy(self, cr, uid, id, default=None, context=None): + if context is None: + context = {} + if default is None: + default = {} + stage = self._get_default_stage_id(cr, uid, context=context) + if stage: + default['stage_id'] = stage + return super(task, self).copy(cr, uid, id, default, context) def _is_template(self, cr, uid, ids, field_name, arg, context=None): res = {} @@ -1248,6 +1258,18 @@ class account_analytic_account(osv.osv): raise osv.except_osv(_('Warning!'), _('Please delete the project linked with this account first.')) return super(account_analytic_account, self).unlink(cr, uid, ids, *args, **kwargs) + def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100): + if args is None: + args = [] + if context is None: + context={} + if context.get('current_model') == 'project.project': + project_ids = self.search(cr, uid, args + [('name', operator, name)], limit=limit, context=context) + return self.name_get(cr, uid, project_ids, context=context) + + return super(account_analytic_account, self).name_search(cr, uid, name, args=args, operator=operator, context=context, limit=limit) + + class project_project(osv.osv): _inherit = 'project.project' _defaults = { diff --git a/addons/project_mrp/process/project_mrp_process.xml b/addons/project_mrp/process/project_mrp_process.xml index c25cd264538..db733d26d15 100644 --- a/addons/project_mrp/process/project_mrp_process.xml +++ b/addons/project_mrp/process/project_mrp_process.xml @@ -51,7 +51,7 @@ - + diff --git a/addons/purchase/partner.py b/addons/purchase/partner.py index 52c5d798eeb..d8cda9b20b9 100644 --- a/addons/purchase/partner.py +++ b/addons/purchase/partner.py @@ -41,7 +41,7 @@ class res_partner(osv.osv): default.update({'purchase_order_ids': []}) - super(res_partner, self).copy(cr, uid, id, default=default, context=context) + return super(res_partner, self).copy(cr, uid, id, default=default, context=context) def _commercial_fields(self, cr, uid, context=None): return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['property_product_pricelist_purchase'] diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 7d2a1282cb0..77c1eae328a 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2053,7 +2053,10 @@ class stock_move(osv.osv): ptype = todo[0][1][5] and todo[0][1][5] or location_obj.picking_type_get(cr, uid, todo[0][0].location_dest_id, todo[0][1][0]) if picking: # name of new picking according to its type - new_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + ptype) + if ptype == 'internal': + new_pick_name = seq_obj.get(cr, uid,'stock.picking') + else : + new_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + ptype) pickid = self._create_chained_picking(cr, uid, new_pick_name, picking, ptype, todo, context=context) # Need to check name of old picking because it always considers picking as "OUT" when created from Sales Order old_ptype = location_obj.picking_type_get(cr, uid, picking.move_lines[0].location_id, picking.move_lines[0].location_dest_id) @@ -2940,8 +2943,8 @@ class stock_warehouse(osv.osv): def _default_lot_output_id(self, cr, uid, context=None): try: - lot_input_stock_model, lot_input_stock_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_output') - self.pool.get('stock.location').check_access_rule(cr, uid, [lot_input_stock_id], 'read', context=context) + lot_output_model, lot_output_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_output') + self.pool.get('stock.location').check_access_rule(cr, uid, [lot_output_id], 'read', context=context) except (ValueError, orm.except_orm): # the user does not have read access on the location or it does not exists lot_output_id = False @@ -2999,6 +3002,18 @@ class stock_picking_in(osv.osv): # instead of its own workflow (which is not existing) return self.pool.get('stock.picking').signal_workflow(cr, uid, ids, signal, context=context) + def message_post(self, *args, **kwargs): + """Post the message on stock.picking to be able to see it in the form view when using the chatter""" + return self.pool.get('stock.picking').message_post(*args, **kwargs) + + def message_subscribe(self, *args, **kwargs): + """Send the subscribe action on stock.picking model as it uses _name in request""" + return self.pool.get('stock.picking').message_subscribe(*args, **kwargs) + + def message_unsubscribe(self, *args, **kwargs): + """Send the unsubscribe action on stock.picking model to match with subscribe""" + return self.pool.get('stock.picking').message_unsubscribe(*args, **kwargs) + _columns = { 'backorder_id': fields.many2one('stock.picking.in', 'Back Order of', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True), 'state': fields.selection( @@ -3060,6 +3075,18 @@ class stock_picking_out(osv.osv): # instead of its own workflow (which is not existing) return self.pool.get('stock.picking').signal_workflow(cr, uid, ids, signal, context=context) + def message_post(self, *args, **kwargs): + """Post the message on stock.picking to be able to see it in the form view when using the chatter""" + return self.pool.get('stock.picking').message_post(*args, **kwargs) + + def message_subscribe(self, *args, **kwargs): + """Send the subscribe action on stock.picking model as it uses _name in request""" + return self.pool.get('stock.picking').message_subscribe(*args, **kwargs) + + def message_unsubscribe(self, *args, **kwargs): + """Send the unsubscribe action on stock.picking model to match with subscribe""" + return self.pool.get('stock.picking').message_unsubscribe(*args, **kwargs) + _columns = { 'backorder_id': fields.many2one('stock.picking.out', 'Back Order of', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True), 'state': fields.selection( diff --git a/addons/stock/stock_demo.xml b/addons/stock/stock_demo.xml index 94844ee8904..eb5a6c46380 100644 --- a/addons/stock/stock_demo.xml +++ b/addons/stock/stock_demo.xml @@ -287,16 +287,6 @@ - - multicomp - multicomp - multicomp - - - - - - diff --git a/addons/stock/tests/test_multicompany.py b/addons/stock/tests/test_multicompany.py index d30e7d0ef2b..fb840a846ea 100644 --- a/addons/stock/tests/test_multicompany.py +++ b/addons/stock/tests/test_multicompany.py @@ -36,7 +36,13 @@ class TestStockMulticompany(common.TransactionCase): self.stock_fill_inventory = self.registry('stock.fill.inventory') self.stock_warehouse = self.registry('stock.warehouse') - model, self.multicompany_user_id = self.ir_model_data.get_object_reference(cr, uid, 'stock', 'multicompany_user') + model, group_user_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') + model, group_stock_manager_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'stock', 'group_stock_manager') + model, company_2_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'stock', 'res_company_2') + self.multicompany_user_id = self.res_users.create(cr, uid, + {'name': 'multicomp', 'login': 'multicomp', + 'groups_id': [(6, 0, [group_user_id, group_stock_manager_id])], + 'company_id': company_2_id, 'company_ids': [(6,0,[company_2_id])]}) def test_00_multicompany_default_stock_move(self): """check no error on getting default stock.move values in multicompany setting""" diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py index 992c64ea134..57ca03a8205 100644 --- a/addons/stock/wizard/stock_return_picking.py +++ b/addons/stock/wizard/stock_return_picking.py @@ -159,13 +159,15 @@ class stock_return_picking(osv.osv_memory): returned_lines = 0 # Create new picking for returned products + + seq_obj_name = 'stock.picking' + new_type = 'internal' if pick.type =='out': new_type = 'in' + seq_obj_name = 'stock.picking.in' elif pick.type =='in': new_type = 'out' - else: - new_type = 'internal' - seq_obj_name = 'stock.picking.' + new_type + seq_obj_name = 'stock.picking.out' new_pick_name = self.pool.get('ir.sequence').get(cr, uid, seq_obj_name) new_picking = pick_obj.copy(cr, uid, pick.id, { 'name': _('%s-%s-return') % (new_pick_name, pick.name),