diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 3c029335fb1..0c3acd98265 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -51,7 +51,12 @@ openerp.base_import = function (instance) { type: 'ir.actions.client', tag: 'import', params: { - model: self.dataset.model + model: self.dataset.model, + // self.dataset.get_context() could be a compound? + // not sure. action's context should be evaluated + // so safer bet. Odd that timezone & al in it + // though + context: self.getParent().action.context, } }, { on_reverse_breadcrumb: function () { @@ -127,6 +132,7 @@ openerp.base_import = function (instance) { var self = this; this._super.apply(this, arguments); this.res_model = action.params.model; + this.parent_context = action.params.context || {}; // import object id this.id = null; this.Import = new instance.web.Model('base_import.import'); @@ -353,11 +359,12 @@ openerp.base_import = function (instance) { }, //- import itself - call_import: function (options) { + call_import: function (kwargs) { var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) { return $(el).select2('val') || false; }).get(); - return this.Import.call('do', [this.id, fields, this.import_options()], options) + kwargs.context = this.parent_context; + return this.Import.call('do', [this.id, fields, this.import_options()], kwargs) .then(undefined, function (error, event) { // In case of unexpected exception, convert // "JSON-RPC error" to an import failure, and diff --git a/addons/board/board.py b/addons/board/board.py index ba5da0db6e1..fafb0eae290 100644 --- a/addons/board/board.py +++ b/addons/board/board.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). -# Copyright (C) 2010-2012 OpenERP s.a. (). +# Copyright (C) 2010-2013 OpenERP s.a. (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -49,6 +49,7 @@ class board_board(osv.osv): ('value', 'in', refs), ], context=context) menu_ids = map(itemgetter('res_id'), IrValues.read(cr, uid, irv_ids, ['res_id'], context=context)) + menu_ids = Menus._filter_visible_menus(cr, uid, menu_ids, context=context) menu_names = Menus.name_get(cr, uid, menu_ids, context=context) return [dict(id=m[0], name=m[1]) for m in menu_names] diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py index 275d9edaab4..04e12c8bce8 100644 --- a/addons/crm_claim/crm_claim.py +++ b/addons/crm_claim/crm_claim.py @@ -188,7 +188,8 @@ class crm_claim(base_stage, osv.osv): through message_process. This override updates the document according to the email. """ - if custom_values is None: custom_values = {} + if custom_values is None: + custom_values = {} desc = html2plaintext(msg.get('body')) if msg.get('body') else '' defaults = { 'name': msg.get('subject') or _("No Subject"), @@ -200,33 +201,7 @@ class crm_claim(base_stage, osv.osv): if msg.get('priority'): defaults['priority'] = msg.get('priority') defaults.update(custom_values) - return super(crm_claim,self).message_new(cr, uid, msg, custom_values=defaults, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: update_vals = {} - - if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): - update_vals['priority'] = msg.get('priority') - - maps = { - 'cost':'planned_cost', - 'revenue': 'planned_revenue', - 'probability':'probability' - } - for line in msg['body'].split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower()): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(crm_claim,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + return super(crm_claim, self).message_new(cr, uid, msg, custom_values=defaults, context=context) class res_partner(osv.osv): _inherit = 'res.partner' diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py index 2d0383962b1..bf5ff8e5185 100644 --- a/addons/crm_helpdesk/crm_helpdesk.py +++ b/addons/crm_helpdesk/crm_helpdesk.py @@ -98,7 +98,8 @@ class crm_helpdesk(base_state, base_stage, osv.osv): through message_process. This override updates the document according to the email. """ - if custom_values is None: custom_values = {} + if custom_values is None: + custom_values = {} desc = html2plaintext(msg.get('body')) if msg.get('body') else '' defaults = { 'name': msg.get('subject') or _("No Subject"), @@ -109,32 +110,6 @@ class crm_helpdesk(base_state, base_stage, osv.osv): 'partner_id': msg.get('author_id', False), } defaults.update(custom_values) - return super(crm_helpdesk,self).message_new(cr, uid, msg, custom_values=defaults, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: update_vals = {} - - if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): - update_vals['priority'] = msg.get('priority') - - maps = { - 'cost':'planned_cost', - 'revenue': 'planned_revenue', - 'probability':'probability' - } - for line in msg['body'].split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower()): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(crm_helpdesk,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + return super(crm_helpdesk, self).message_new(cr, uid, msg, custom_values=defaults, context=context) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm_partner_assign/crm_partner_assign.py b/addons/crm_partner_assign/crm_partner_assign.py index b13a75a2591..3e3795eb653 100644 --- a/addons/crm_partner_assign/crm_partner_assign.py +++ b/addons/crm_partner_assign/crm_partner_assign.py @@ -168,6 +168,12 @@ class crm_lead(osv.osv): return res def assign_geo_localize(self, cr, uid, ids, latitude=False, longitude=False, context=None): + if latitude and longitude: + self.write(cr, uid, ids, { + 'partner_latitude': latitude, + 'partner_longitude': longitude + }, context=context) + return True # Don't pass context to browse()! We need country name in english below for lead in self.browse(cr, uid, ids): if not lead.country_id: @@ -177,14 +183,11 @@ class crm_lead(osv.osv): city=lead.city, state=lead.state_id.name, country=lead.country_id.name)) - if not latitude and result: - latitude = result[0] - if not longitude and result: - longitude = result[1] - self.write(cr, uid, [lead.id], { - 'partner_latitude': latitude, - 'partner_longitude': longitude - }, context=context) + if result: + self.write(cr, uid, [lead.id], { + 'partner_latitude': result[0], + 'partner_longitude': result[1] + }, context=context) return True def search_geo_partner(self, cr, uid, ids, context=None): diff --git a/addons/document/static/src/js/document.js b/addons/document/static/src/js/document.js index 23f1375e8c8..e4bce2da76e 100644 --- a/addons/document/static/src/js/document.js +++ b/addons/document/static/src/js/document.js @@ -9,7 +9,7 @@ openerp.document = function (instance) { on_attachments_loaded: function(attachments) { //to display number in name if more then one attachment which has same name. var self = this; - _.chain(attachments.reverse()) + _.chain(attachments) .groupBy(function(attachment) { return attachment.name}) .each(function(attachment){ if(attachment.length > 1) diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 39ed96927bd..d2a59b027bb 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -352,7 +352,8 @@ class hr_applicant(base_stage, osv.Model): through message_process. This override updates the document according to the email. """ - if custom_values is None: custom_values = {} + if custom_values is None: + custom_values = {} desc = html2plaintext(msg.get('body')) if msg.get('body') else '' defaults = { 'name': msg.get('subject') or _("No Subject"), @@ -365,38 +366,7 @@ class hr_applicant(base_stage, osv.Model): if msg.get('priority'): defaults['priority'] = msg.get('priority') defaults.update(custom_values) - return super(hr_applicant,self).message_new(cr, uid, msg, custom_values=defaults, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Override mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: - update_vals = {} - - update_vals.update({ - 'email_from': msg.get('from'), - 'email_cc': msg.get('cc'), - }) - if msg.get('priority'): - update_vals['priority'] = msg.get('priority') - - maps = { - 'cost': 'planned_cost', - 'revenue': 'planned_revenue', - 'probability': 'probability', - } - for line in msg.get('body', '').split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower(), False): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(hr_applicant, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + return super(hr_applicant, self).message_new(cr, uid, msg, custom_values=defaults, context=context) def create(self, cr, uid, vals, context=None): if context is None: diff --git a/addons/l10n_multilang/l10n_multilang.py b/addons/l10n_multilang/l10n_multilang.py index b7564a9960a..b087d94f7b6 100644 --- a/addons/l10n_multilang/l10n_multilang.py +++ b/addons/l10n_multilang/l10n_multilang.py @@ -45,8 +45,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): :param in_ids: List of ids of source object :param out_obj: Destination object for which translation is to be copied :param out_ids: List of ids of destination object - :param force_write: boolean that depicts if we need to create a translation OR simply replace the actual value - with the translation in the uid's language by doing a write (in case it's TRUE) + :param force_write: Deprecated as of 7.0, do not use :param context: usual context information. May contain the key 'lang', which is the language of the user running the wizard, that will be used if force_write is True @@ -65,26 +64,25 @@ class wizard_multi_charts_accounts(osv.osv_memory): for j in range(len(in_ids)): in_id = in_ids[j] if value[in_id]: - if not force_write: - #copy Translation from Source to Destination object - xlat_obj.create(cr, uid, { - 'name': out_obj._name + ',' + in_field, - 'type': 'model', - 'res_id': out_ids[j], - 'lang': lang, - 'src': src[in_id], - 'value': value[in_id], + #copy Translation from Source to Destination object + xlat_obj.create(cr, uid, { + 'name': out_obj._name + ',' + in_field, + 'type': 'model', + 'res_id': out_ids[j], + 'lang': lang, + 'src': src[in_id], + 'value': value[in_id], }) - else: - #replace the value in the destination object only if it's the user lang - if context.get('lang') == lang: - self.pool[out_obj._name].write(cr, uid, out_ids[j], {in_field: value[in_id]}) else: _logger.info('Language: %s. Translation from template: there is no translation available for %s!' %(lang, src[in_id]))#out_obj._name)) return True def execute(self, cr, uid, ids, context=None): - res = super(wizard_multi_charts_accounts, self).execute(cr, uid, ids, context=context) + if not context: + context = {} + # remove the lang to get the untranslated value + ctx = dict(context, lang=None) + res = super(wizard_multi_charts_accounts, self).execute(cr, uid, ids, context=ctx) obj_multi = self.browse(cr, uid, ids[0], context=context) company_id = obj_multi.company_id.id @@ -125,7 +123,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): acc_root_id = obj_acc.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', None)])[0] in_ids = obj_acc_template.search(cr, uid, [('id', 'child_of', [acc_template_root_id])], order='id')[1:] out_ids = obj_acc.search(cr, uid, [('id', 'child_of', [acc_root_id])], order='id')[1:] - return self.process_translations(cr, uid, langs, obj_acc_template, field, in_ids, obj_acc, out_ids, force_write=True, context=context) + return self.process_translations(cr, uid, langs, obj_acc_template, field, in_ids, obj_acc, out_ids, context=context) def _process_tax_codes_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None): obj_tax_code_template = self.pool.get('account.tax.code.template') @@ -134,21 +132,21 @@ class wizard_multi_charts_accounts(osv.osv_memory): tax_code_root_id = obj_tax_code.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', None)])[0] in_ids = obj_tax_code_template.search(cr, uid, [('id', 'child_of', [tax_code_template_root_id])], order='id')[1:] out_ids = obj_tax_code.search(cr, uid, [('id', 'child_of', [tax_code_root_id])], order='id')[1:] - return self.process_translations(cr, uid, langs, obj_tax_code_template, field, in_ids, obj_tax_code, out_ids, force_write=False, context=context) + return self.process_translations(cr, uid, langs, obj_tax_code_template, field, in_ids, obj_tax_code, out_ids, context=context) def _process_taxes_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None): obj_tax_template = self.pool.get('account.tax.template') obj_tax = self.pool.get('account.tax') in_ids = sorted([x.id for x in obj_multi.chart_template_id.tax_template_ids]) out_ids = obj_tax.search(cr, uid, [('company_id', '=', company_id)], order='id') - return self.process_translations(cr, uid, langs, obj_tax_template, field, in_ids, obj_tax, out_ids, force_write=False, context=context) + return self.process_translations(cr, uid, langs, obj_tax_template, field, in_ids, obj_tax, out_ids, context=context) def _process_fiscal_pos_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None): obj_fiscal_position_template = self.pool.get('account.fiscal.position.template') obj_fiscal_position = self.pool.get('account.fiscal.position') in_ids = obj_fiscal_position_template.search(cr, uid, [('chart_template_id', '=', obj_multi.chart_template_id.id)], order='id') out_ids = obj_fiscal_position.search(cr, uid, [('company_id', '=', company_id)], order='id') - return self.process_translations(cr, uid, langs, obj_fiscal_position_template, field, in_ids, obj_fiscal_position, out_ids, force_write=False, context=context) + return self.process_translations(cr, uid, langs, obj_fiscal_position_template, field, in_ids, obj_fiscal_position, out_ids, context=context) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project/project.py b/addons/project/project.py index 0528dc8aae3..e3acf1e1a90 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -1232,7 +1232,8 @@ class task(base_stage, osv.osv): def message_new(self, cr, uid, msg, custom_values=None, context=None): """ Override to updates the document according to the email. """ - if custom_values is None: custom_values = {} + if custom_values is None: + custom_values = {} defaults = { 'name': msg.get('subject'), 'planned_hours': 0.0, @@ -1242,10 +1243,10 @@ class task(base_stage, osv.osv): def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): """ Override to update the task according to the email. """ - if update_vals is None: update_vals = {} - act = False + if update_vals is None: + update_vals = {} maps = { - 'cost':'planned_hours', + 'cost': 'planned_hours', } for line in msg['body'].split('\n'): line = line.strip() @@ -1258,12 +1259,7 @@ class task(base_stage, osv.osv): update_vals[field] = float(res.group(2).lower()) except (ValueError, TypeError): pass - elif match.lower() == 'state' \ - and res.group(2).lower() in ['cancel','close','draft','open','pending']: - act = 'do_%s' % res.group(2).lower() - if act: - getattr(self,act)(cr, uid, ids, context=context) - return super(task,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + return super(task, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) def project_task_reevaluate(self, cr, uid, ids, context=None): if self.pool.get('res.users').has_group(cr, uid, 'project.group_time_work_estimation_tasks'): diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 48bf1457d27..a89d530575e 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -537,8 +537,10 @@ class project_issue(base_stage, osv.osv): through message_process. This override updates the document according to the email. """ - if custom_values is None: custom_values = {} - if context is None: context = {} + if custom_values is None: + custom_values = {} + if context is None: + context = {} context['state_to'] = 'draft' desc = html2plaintext(msg.get('body')) if msg.get('body') else '' @@ -551,40 +553,10 @@ class project_issue(base_stage, osv.osv): 'partner_id': msg.get('author_id', False), 'user_id': False, } - if msg.get('priority'): - defaults['priority'] = msg.get('priority') - defaults.update(custom_values) res_id = super(project_issue, self).message_new(cr, uid, msg, custom_values=defaults, context=context) return res_id - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: update_vals = {} - - # Update doc values according to the message - if msg.get('priority'): - update_vals['priority'] = msg.get('priority') - # Parse 'body' to find values to update - maps = { - 'cost': 'planned_cost', - 'revenue': 'planned_revenue', - 'probability': 'probability', - } - for line in msg.get('body', '').split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower(), False): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(project_issue, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) - def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, content_subtype='html', **kwargs): """ Overrides mail_thread message_post so that we can set the date of last action field when a new message is posted on the issue. diff --git a/addons/purchase/report/purchase_report.py b/addons/purchase/report/purchase_report.py index 5af74b23495..f6a37bb37a7 100644 --- a/addons/purchase/report/purchase_report.py +++ b/addons/purchase/report/purchase_report.py @@ -94,23 +94,20 @@ class purchase_report(osv.osv): extract(epoch from age(s.date_approve,s.date_order))/(24*60*60)::decimal(16,2) as delay, extract(epoch from age(l.date_planned,s.date_order))/(24*60*60)::decimal(16,2) as delay_pass, count(*) as nbr, - (l.price_unit*l.product_qty)::decimal(16,2) as price_total, + sum(l.price_unit*l.product_qty)::decimal(16,2) as price_total, avg(100.0 * (l.price_unit*l.product_qty) / NULLIF(t.standard_price*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation, - sum(t.standard_price*l.product_qty/u.factor*u2.factor)::decimal(16,2) as price_standard, (sum(l.product_qty*l.price_unit)/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average - from purchase_order s - left join purchase_order_line l on (s.id=l.order_id) + from purchase_order_line l + join purchase_order s on (l.order_id=s.id) left join product_product p on (l.product_id=p.id) left join product_template t on (p.product_tmpl_id=t.id) left join product_uom u on (u.id=l.product_uom) left join product_uom u2 on (u2.id=t.uom_id) - where l.product_id is not null group by s.company_id, s.create_uid, s.partner_id, - l.product_qty, u.factor, s.location_id, l.price_unit, diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index 80143379247..6091d708979 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -71,7 +71,7 @@ class sale_report(osv.osv): t.uom_id as product_uom, sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty, sum(l.product_uom_qty * l.price_unit * (100.0-l.discount) / 100.0) as price_total, - 1 as nbr, + count(*) as nbr, s.date_order as date, s.date_confirm as date_confirm, to_char(s.date_order, 'YYYY') as year, @@ -87,15 +87,14 @@ class sale_report(osv.osv): s.pricelist_id as pricelist_id, s.project_id as analytic_account_id from - sale_order s - join sale_order_line l on (s.id=l.order_id) + sale_order_line l + join sale_order s on (l.order_id=s.id) left join product_product p on (l.product_id=p.id) left join product_template t on (p.product_tmpl_id=t.id) left join product_uom u on (u.id=l.product_uom) left join product_uom u2 on (u2.id=t.uom_id) group by l.product_id, - l.product_uom_qty, l.order_id, t.uom_id, t.categ_id, diff --git a/addons/sale_stock/report/sale_report.py b/addons/sale_stock/report/sale_report.py index 63641020d98..b06449cf223 100644 --- a/addons/sale_stock/report/sale_report.py +++ b/addons/sale_stock/report/sale_report.py @@ -41,6 +41,8 @@ class sale_report(osv.osv): def init(self, cr): tools.drop_view_if_exists(cr, 'sale_report') + # TODO: make parent view extensible similarly to invoice analysis and + # remove the duplication cr.execute(""" create or replace view sale_report as ( select @@ -49,7 +51,7 @@ class sale_report(osv.osv): t.uom_id as product_uom, sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty, sum(l.product_uom_qty * l.price_unit * (100.0-l.discount) / 100.0) as price_total, - 1 as nbr, + count(*) as nbr, s.date_order as date, s.date_confirm as date_confirm, to_char(s.date_order, 'YYYY') as year, @@ -67,15 +69,14 @@ class sale_report(osv.osv): s.pricelist_id as pricelist_id, s.project_id as analytic_account_id from - sale_order s - join sale_order_line l on (s.id=l.order_id) + sale_order_line l + join sale_order s on (l.order_id=s.id) left join product_product p on (l.product_id=p.id) left join product_template t on (p.product_tmpl_id=t.id) left join product_uom u on (u.id=l.product_uom) left join product_uom u2 on (u2.id=t.uom_id) group by l.product_id, - l.product_uom_qty, l.order_id, t.uom_id, t.categ_id, diff --git a/addons/stock/stock.py b/addons/stock/stock.py index d818ca794d5..cd19bbf40a5 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -399,15 +399,17 @@ class stock_location(osv.osv): uom_rounding = self.pool.get('product.product').browse(cr, uid, product_id, context=context).uom_id.rounding if context.get('uom'): uom_rounding = uom_obj.browse(cr, uid, context.get('uom'), context=context).rounding + prodlot_id = context.get('prodlot_id', False) locations_ids = self.search(cr, uid, [('location_id', 'child_of', ids)]) if locations_ids: # Fetch only the locations in which this product has ever been processed (in or out) cr.execute("""SELECT l.id FROM stock_location l WHERE l.id in %s AND EXISTS (SELECT 1 FROM stock_move m WHERE m.product_id = %s + AND (NOT BOOL(%s) OR m.prodlot_id=%s) AND ((state = 'done' AND m.location_dest_id = l.id) OR (state in ('done','assigned') AND m.location_id = l.id))) - """, (tuple(locations_ids), product_id,)) + """, (tuple(locations_ids), product_id, prodlot_id, prodlot_id or None)) locations_ids = [i for (i,) in cr.fetchall()] for id in locations_ids: if lock: @@ -419,6 +421,7 @@ class stock_location(osv.osv): cr.execute("SAVEPOINT stock_location_product_reserve") cr.execute("""SELECT id FROM stock_move WHERE product_id=%s AND + (NOT BOOL(%s) OR prodlot_id=%s) AND ( (location_dest_id=%s AND location_id<>%s AND @@ -428,7 +431,7 @@ class stock_location(osv.osv): location_dest_id<>%s AND state in ('done', 'assigned')) ) - FOR UPDATE of stock_move NOWAIT""", (product_id, id, id, id, id), log_exceptions=False) + FOR UPDATE of stock_move NOWAIT""", (product_id, prodlot_id, prodlot_id or None, id, id, id, id), log_exceptions=False) except Exception: # Here it's likely that the FOR UPDATE NOWAIT failed to get the LOCK, # so we ROLLBACK to the SAVEPOINT to restore the transaction to its earlier @@ -444,20 +447,22 @@ class stock_location(osv.osv): WHERE location_dest_id=%s AND location_id<>%s AND product_id=%s AND + (NOT BOOL(%s) OR prodlot_id=%s) AND state='done' GROUP BY product_uom """, - (id, id, product_id)) + (id, id, product_id, prodlot_id, prodlot_id or None)) results = cr.dictfetchall() cr.execute("""SELECT product_uom,-sum(product_qty) AS product_qty FROM stock_move WHERE location_id=%s AND location_dest_id<>%s AND product_id=%s AND + (NOT BOOL(%s) OR prodlot_id=%s) AND state in ('done', 'assigned') GROUP BY product_uom """, - (id, id, product_id)) + (id, id, product_id, prodlot_id, prodlot_id or None)) results += cr.dictfetchall() total = 0.0 results2 = 0.0 @@ -1263,17 +1268,17 @@ class stock_picking(osv.osv): context['currency_id'] = move_currency_id qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id) - if product.id in product_avail: - product_avail[product.id] += qty - else: + if product.id not in product_avail: + # keep track of stock on hand including processed lines not yet marked as done product_avail[product.id] = product.qty_available if qty > 0: new_price = currency_obj.compute(cr, uid, product_currency, - move_currency_id, product_price) + move_currency_id, product_price, round=False) new_price = uom_obj._compute_price(cr, uid, product_uom, new_price, product.uom_id.id) - if product.qty_available <= 0: + if product_avail[product.id] <= 0: + product_avail[product.id] = 0 new_std_price = new_price else: # Get the standard price @@ -1289,6 +1294,9 @@ class stock_picking(osv.osv): {'price_unit': product_price, 'price_currency_id': product_currency}) + product_avail[product.id] += qty + + for move in too_few: product_qty = move_product_qty[move.id] @@ -2166,8 +2174,12 @@ class stock_move(osv.osv): pickings[move.picking_id.id] = 1 continue if move.state in ('confirmed', 'waiting'): + ctx = context.copy() + ctx.update({'uom': move.product_uom.id}) + if move.prodlot_id: + ctx.update({'prodlot_id': move.prodlot_id.id}) # Important: we must pass lock=True to _product_reserve() to avoid race conditions and double reservations - res = self.pool.get('stock.location')._product_reserve(cr, uid, [move.location_id.id], move.product_id.id, move.product_qty, {'uom': move.product_uom.id}, lock=True) + res = self.pool.get('stock.location')._product_reserve(cr, uid, [move.location_id.id], move.product_id.id, move.product_qty, context=ctx, lock=True) if res: #_product_available_test depends on the next status for correct functioning #the test does not work correctly if the same product occurs multiple times @@ -2693,7 +2705,7 @@ class stock_move(osv.osv): qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id) if qty > 0: new_price = currency_obj.compute(cr, uid, product_currency, - move_currency_id, product_price) + move_currency_id, product_price, round=False) new_price = uom_obj._compute_price(cr, uid, product_uom, new_price, product.uom_id.id) if product.qty_available <= 0: diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py index 8c2c5839989..50ddc2b88ed 100644 --- a/addons/stock/wizard/stock_partial_picking.py +++ b/addons/stock/wizard/stock_partial_picking.py @@ -51,7 +51,7 @@ class stock_partial_picking_line(osv.TransientModel): 'move_id' : fields.many2one('stock.move', "Move", ondelete='CASCADE'), 'wizard_id' : fields.many2one('stock.partial.picking', string="Wizard", ondelete='CASCADE'), 'update_cost': fields.boolean('Need cost update'), - 'cost' : fields.float("Cost", help="Unit Cost for this product line"), + 'cost' : fields.float("Cost", help="Unit Cost for this product line", digits_compute=dp.get_precision('Product Price')), 'currency' : fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'), 'tracking': fields.function(_tracking, string='Tracking', type='boolean'), }