From 16a23041b25b6568f98e46e5f6323dea610c50a4 Mon Sep 17 00:00:00 2001 From: Ravi Gohil Date: Fri, 17 Oct 2014 19:00:49 +0530 Subject: [PATCH 01/52] [FIX] point_of_sale: fixed rounding issue for pos order when discount added(maintenance: 615322). --- addons/point_of_sale/static/src/js/models.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index b7b36d05b4e..606ee1e78c0 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -494,7 +494,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal get_all_prices: function(){ var self = this; var currency_rounding = this.pos.get('currency').rounding; - var base = round_pr(this.get_quantity() * this.get_unit_price() * (1.0 - (this.get_discount() / 100.0)), currency_rounding); + var base = round_pr(round_pr(this.get_quantity() * this.get_unit_price(), currency_rounding) * (1.0 - (this.get_discount() / 100.0)), currency_rounding); var totalTax = base; var totalNoTax = base; From e4fdd85424b4e205024ebf6353a4780871af9907 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 6 Nov 2014 17:41:23 +0100 Subject: [PATCH 02/52] [FIX] hr,mail: correct remove of suggestions. --- addons/hr/static/src/xml/suggestions.xml | 2 +- addons/mail/static/src/xml/suggestions.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/hr/static/src/xml/suggestions.xml b/addons/hr/static/src/xml/suggestions.xml index f3095627bae..00fdb9b19cd 100644 --- a/addons/hr/static/src/xml/suggestions.xml +++ b/addons/hr/static/src/xml/suggestions.xml @@ -24,7 +24,7 @@
- X + X
diff --git a/addons/mail/static/src/xml/suggestions.xml b/addons/mail/static/src/xml/suggestions.xml index 9157dcbe32e..fbd983b767f 100644 --- a/addons/mail/static/src/xml/suggestions.xml +++ b/addons/mail/static/src/xml/suggestions.xml @@ -25,7 +25,7 @@
- X + X
From c6a5be0a195c71c7ded9cc863815d115259abfd9 Mon Sep 17 00:00:00 2001 From: Ravi Gohil Date: Tue, 4 Nov 2014 15:33:25 +0530 Subject: [PATCH 03/52] [IMP] stock: added missing 'context' parameter (opw 616952) --- addons/stock/wizard/stock_partial_move.py | 2 +- addons/stock/wizard/stock_partial_picking.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/stock/wizard/stock_partial_move.py b/addons/stock/wizard/stock_partial_move.py index e1956e202c9..baba6e7792b 100644 --- a/addons/stock/wizard/stock_partial_move.py +++ b/addons/stock/wizard/stock_partial_move.py @@ -53,7 +53,7 @@ class stock_partial_move(osv.osv_memory): return res if 'move_ids' in fields: move_ids = self.pool.get('stock.move').browse(cr, uid, move_ids, context=context) - moves = [self._partial_move_for(cr, uid, m) for m in move_ids if m.state not in ('done','cancel')] + moves = [self._partial_move_for(cr, uid, m, context=context) for m in move_ids if m.state not in ('done','cancel')] res.update(move_ids=moves) if 'date' in fields: res.update(date=time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)) diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py index 5419d2dda2a..b67c126e775 100644 --- a/addons/stock/wizard/stock_partial_picking.py +++ b/addons/stock/wizard/stock_partial_picking.py @@ -130,7 +130,7 @@ class stock_partial_picking(osv.osv_memory): res.update(picking_id=picking_id) if 'move_ids' in fields: picking = self.pool.get('stock.picking').browse(cr, uid, picking_id, context=context) - moves = [self._partial_move_for(cr, uid, m) for m in picking.move_lines if m.state not in ('done','cancel')] + moves = [self._partial_move_for(cr, uid, m, context=context) for m in picking.move_lines if m.state not in ('done','cancel')] res.update(move_ids=moves) if 'date' in fields: res.update(date=time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)) @@ -154,7 +154,7 @@ class stock_partial_picking(osv.osv_memory): return {'cost': move.product_id.standard_price, 'currency': product_currency_id or picking_currency_id or False} - def _partial_move_for(self, cr, uid, move): + def _partial_move_for(self, cr, uid, move, context=None): partial_move = { 'product_id' : move.product_id.id, 'quantity' : move.product_qty if move.state == 'assigned' or move.picking_id.type == 'in' else 0, From c200ffd74e892492994fa66ac8f38b390bc2b8d9 Mon Sep 17 00:00:00 2001 From: Dhs-odoo Date: Mon, 10 Nov 2014 14:13:43 +0100 Subject: [PATCH 04/52] [FIX] project_timesheet: missing timesheet for tasks without project Timesheet activities (hr.analytic.timesheet) are generated when a work activity (project.task.work) is logged on a task. These are updated if the project of the task is modified. This patch applies the same behaviour for tasks without project, the timesheet activities are generated once a project is set on the task. To avoid redundency in the code, extract the computation in a distinct method. Fixes #701, opw 609481 --- addons/project_timesheet/project_timesheet.py | 108 +++++++++++------- 1 file changed, 68 insertions(+), 40 deletions(-) diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py index 5945d52c5c2..b6988590acb 100644 --- a/addons/project_timesheet/project_timesheet.py +++ b/addons/project_timesheet/project_timesheet.py @@ -105,49 +105,56 @@ class project_work(osv.osv): res['product_uom_id'] = emp.product_id.uom_id.id return res - def create(self, cr, uid, vals, *args, **kwargs): - timesheet_obj = self.pool.get('hr.analytic.timesheet') - task_obj = self.pool.get('project.task') - uom_obj = self.pool.get('product.uom') + def _create_analytic_entries(self, cr, uid, vals, context): + """Create the hr analytic timesheet from project task work""" + timesheet_obj = self.pool['hr.analytic.timesheet'] + task_obj = self.pool['project.task'] vals_line = {} + timeline_id = False + acc_id = False + + task_obj = task_obj.browse(cr, uid, vals['task_id'], context=context) + result = self.get_user_related_details(cr, uid, vals.get('user_id', uid)) + vals_line['name'] = '%s: %s' % (tools.ustr(task_obj.name), tools.ustr(vals['name'] or '/')) + vals_line['user_id'] = vals['user_id'] + vals_line['product_id'] = result['product_id'] + vals_line['date'] = vals['date'][:10] + + # Calculate quantity based on employee's product's uom + vals_line['unit_amount'] = vals['hours'] + + default_uom = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.project_time_mode_id.id + if result['product_uom_id'] != default_uom: + vals_line['unit_amount'] = self.pool['product.uom']._compute_qty(cr, uid, default_uom, vals['hours'], result['product_uom_id']) + acc_id = task_obj.project_id and task_obj.project_id.analytic_account_id.id or acc_id + if acc_id: + vals_line['account_id'] = acc_id + res = timesheet_obj.on_change_account_id(cr, uid, False, acc_id) + if res.get('value'): + vals_line.update(res['value']) + vals_line['general_account_id'] = result['general_account_id'] + vals_line['journal_id'] = result['journal_id'] + vals_line['amount'] = 0.0 + vals_line['product_uom_id'] = result['product_uom_id'] + amount = vals_line['unit_amount'] + prod_id = vals_line['product_id'] + unit = False + timeline_id = timesheet_obj.create(cr, uid, vals=vals_line, context=context) + + # Compute based on pricetype + amount_unit = timesheet_obj.on_change_unit_amount(cr, uid, timeline_id, + prod_id, amount, False, unit, vals_line['journal_id'], context=context) + if amount_unit and 'amount' in amount_unit.get('value',{}): + updv = { 'amount': amount_unit['value']['amount'] } + timesheet_obj.write(cr, uid, [timeline_id], updv, context=context) + + return timeline_id + + def create(self, cr, uid, vals, *args, **kwargs): context = kwargs.get('context', {}) if not context.get('no_analytic_entry',False): - task_obj = task_obj.browse(cr, uid, vals['task_id']) - result = self.get_user_related_details(cr, uid, vals.get('user_id', uid)) - vals_line['name'] = '%s: %s' % (tools.ustr(task_obj.name), tools.ustr(vals['name'] or '/')) - vals_line['user_id'] = vals['user_id'] - vals_line['product_id'] = result['product_id'] - vals_line['date'] = vals['date'][:10] - - # Calculate quantity based on employee's product's uom - vals_line['unit_amount'] = vals['hours'] - - default_uom = self.pool.get('res.users').browse(cr, uid, uid).company_id.project_time_mode_id.id - if result['product_uom_id'] != default_uom: - vals_line['unit_amount'] = uom_obj._compute_qty(cr, uid, default_uom, vals['hours'], result['product_uom_id']) - acc_id = task_obj.project_id and task_obj.project_id.analytic_account_id.id or False - if acc_id: - vals_line['account_id'] = acc_id - res = timesheet_obj.on_change_account_id(cr, uid, False, acc_id) - if res.get('value'): - vals_line.update(res['value']) - vals_line['general_account_id'] = result['general_account_id'] - vals_line['journal_id'] = result['journal_id'] - vals_line['amount'] = 0.0 - vals_line['product_uom_id'] = result['product_uom_id'] - amount = vals_line['unit_amount'] - prod_id = vals_line['product_id'] - unit = False - timeline_id = timesheet_obj.create(cr, uid, vals=vals_line, context=context) - - # Compute based on pricetype - amount_unit = timesheet_obj.on_change_unit_amount(cr, uid, timeline_id, - prod_id, amount, False, unit, vals_line['journal_id'], context=context) - if amount_unit and 'amount' in amount_unit.get('value',{}): - updv = { 'amount': amount_unit['value']['amount'] } - timesheet_obj.write(cr, uid, [timeline_id], updv, context=context) - vals['hr_analytic_timesheet_id'] = timeline_id + vals['hr_analytic_timesheet_id'] = self._create_analytic_entries(cr, uid, vals, context=context) return super(project_work,self).create(cr, uid, vals, *args, **kwargs) def write(self, cr, uid, ids, vals, context=None): @@ -236,6 +243,10 @@ class task(osv.osv): def write(self, cr, uid, ids, vals, context=None): if context is None: context = {} + task_work_obj = self.pool['project.task.work'] + acc_id = False + missing_analytic_entries = {} + if vals.get('project_id',False) or vals.get('name',False): vals_line = {} hr_anlytic_timesheet = self.pool.get('hr.analytic.timesheet') @@ -247,6 +258,16 @@ class task(osv.osv): if len(task_obj.work_ids): for task_work in task_obj.work_ids: if not task_work.hr_analytic_timesheet_id: + if acc_id : + # missing timesheet activities to generate + missing_analytic_entries[task_work.id] = { + 'name' : task_work.name, + 'user_id' : task_work.user_id.id, + 'date' : task_work.date[:10], + 'account_id': acc_id, + 'hours' : task_work.hours, + 'task_id' : task_obj.id + } continue line_id = task_work.hr_analytic_timesheet_id.id if vals.get('project_id',False): @@ -254,7 +275,14 @@ class task(osv.osv): if vals.get('name',False): vals_line['name'] = '%s: %s' % (tools.ustr(vals['name']), tools.ustr(task_work.name) or '/') hr_anlytic_timesheet.write(cr, uid, [line_id], vals_line, {}) - return super(task,self).write(cr, uid, ids, vals, context) + + res = super(task,self).write(cr, uid, ids, vals, context) + + for task_work_id, analytic_entry in missing_analytic_entries.items(): + timeline_id = task_work_obj._create_analytic_entries(cr, uid, analytic_entry, context=context) + task_work_obj.write(cr, uid, task_work_id, {'hr_analytic_timesheet_id' : timeline_id}, context=context) + + return res task() From e27afc13cb5d95831cb9f4a0f1fb98e70129ceed Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Mon, 10 Nov 2014 15:36:40 +0100 Subject: [PATCH 05/52] [FIX] mrp: prevent suppression of bom if used in mo The field bom_id is required on a manufacturing order and deleting a mrp.bom would block the current mo. Restrict the suppression for manufacturing order in progress. Fixes #3417 --- addons/mrp/mrp.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 72226e9de60..45b2bcbc047 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -379,6 +379,13 @@ class mrp_bom(osv.osv): default.update(name=_("%s (copy)") % (bom_data['name']), bom_id=False) return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context) + def unlink(self, cr, uid, ids, context=None): + if self.pool['mrp.production'].search(cr, uid, [ + ('bom_id', 'in', ids), ('state', 'not in', ['done', 'cancel']) + ], context=context): + raise osv.except_osv(_('Warning!'), _('You can not delete a Bill of Material with running manufacturing orders.\nPlease close or cancel it first.')) + return super(mrp_bom, self).unlink(cr, uid, ids, context=context) + def rounding(f, r): # TODO for trunk: log deprecation warning From ab5ecef476b82c7e00dbd1868de19832ac044f15 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Mon, 10 Nov 2014 15:59:30 +0100 Subject: [PATCH 06/52] [FIX] project_timesheet: accept work without date date field on a project.task.work is not required while it is on the hr.analytic.timesheet (with default value). Avoid error if fill a task work without date, fallback on context_today. --- addons/project_timesheet/project_timesheet.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py index b6988590acb..6cbb4e8cf01 100644 --- a/addons/project_timesheet/project_timesheet.py +++ b/addons/project_timesheet/project_timesheet.py @@ -119,7 +119,8 @@ class project_work(osv.osv): vals_line['name'] = '%s: %s' % (tools.ustr(task_obj.name), tools.ustr(vals['name'] or '/')) vals_line['user_id'] = vals['user_id'] vals_line['product_id'] = result['product_id'] - vals_line['date'] = vals['date'][:10] + if vals.get('date'): + vals_line['date' ] = vals['date'][:10] # Calculate quantity based on employee's product's uom vals_line['unit_amount'] = vals['hours'] @@ -263,7 +264,7 @@ class task(osv.osv): missing_analytic_entries[task_work.id] = { 'name' : task_work.name, 'user_id' : task_work.user_id.id, - 'date' : task_work.date[:10], + 'date' : task_work.date and task_work.date[:10] or False, 'account_id': acc_id, 'hours' : task_work.hours, 'task_id' : task_obj.id From ca209eb18411847783559012107c93a2e76cfadc Mon Sep 17 00:00:00 2001 From: Rucha Dave Date: Mon, 10 Nov 2014 11:27:20 +0530 Subject: [PATCH 07/52] [FIX] hr_timesheet(_sheet): access rights on report hr.timesheet.report was moved from hr_timesheet_sheet to hr_timesheet but not the ir.model.access Avoid warning "The model hr.timesheet.report has no access rules, consider adding one." Fixes #3319 --- addons/hr_timesheet/security/ir.model.access.csv | 1 + addons/hr_timesheet_sheet/security/ir.model.access.csv | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_timesheet/security/ir.model.access.csv b/addons/hr_timesheet/security/ir.model.access.csv index 689d425054a..4ac30f025ba 100644 --- a/addons/hr_timesheet/security/ir.model.access.csv +++ b/addons/hr_timesheet/security/ir.model.access.csv @@ -10,3 +10,4 @@ access_product_uom_hr_timesheet,product.uom.hr.timesheet,product.model_product_u access_account_fiscalyear_hr_user,account.account.fiscalyear.user,account.model_account_fiscalyear,base.group_hr_user,1,1,1,1 access_hr_analytic_timesheet_user,hr.analytic.timesheet.user,model_hr_analytic_timesheet,base.group_user,1,1,1,0 access_account_analytic_journal_user,account.account.analytic.journal.user,account.model_account_analytic_journal,base.group_user,1,0,0,0 +access_hr_timesheet_report,access_hr_timesheet_report,model_hr_timesheet_report,,1,1,1,1 diff --git a/addons/hr_timesheet_sheet/security/ir.model.access.csv b/addons/hr_timesheet_sheet/security/ir.model.access.csv index 8eef30b35c4..2ad3c7d5ca2 100644 --- a/addons/hr_timesheet_sheet/security/ir.model.access.csv +++ b/addons/hr_timesheet_sheet/security/ir.model.access.csv @@ -3,6 +3,5 @@ access_hr_timesheet_sheet_sheet_user,hr_timesheet_sheet.sheet.user,model_hr_time access_hr_timesheet_sheet_sheet_system_employee,hr_timesheet_sheet.sheet.system.employee,model_hr_timesheet_sheet_sheet,base.group_user,1,1,1,0 access_hr_timesheet_sheet_sheet_day,hr_timesheet_sheet.sheet.day,model_hr_timesheet_sheet_sheet_day,base.group_hr_user,1,1,1,1 access_hr_timesheet_sheet_sheet_account,hr_timesheet_sheet.sheet.account,model_hr_timesheet_sheet_sheet_account,base.group_hr_user,1,1,1,1 -access_hr_timesheet_report,hr.timesheet.report,model_hr_timesheet_report,base.group_hr_manager,1,1,0,0 access_hr_analytic_timesheet_system_user,hr.analytic.timesheet.system.user,model_hr_analytic_timesheet,base.group_user,1,0,0,0 access_hr_timesheet_sheet_sheet_day,hr.timesheet.sheet.sheet.day.user,model_hr_timesheet_sheet_sheet_day,base.group_user,1,1,1,0 From 69d60465ee67969d72210a61a9e23204d037378a Mon Sep 17 00:00:00 2001 From: Rifakat Haradwala Date: Wed, 22 Oct 2014 15:37:00 +0530 Subject: [PATCH 08/52] [FIX] web: date autocompletion should use user's locale search bar does not suggest date field format based on user's locale and always shows based on mmddyy using Date.parse, opw:615276 Note: starting in 9.0, datejs has been replaced by momentjs, so this problem should be solved in a better way. --- addons/web/static/src/js/search.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index c09661f430d..89cf5521db1 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1497,7 +1497,11 @@ instance.web.search.DateField = instance.web.search.Field.extend(/** @lends inst return instance.web.date_to_str(facetValue.get('value')); }, complete: function (needle) { - var d = Date.parse(needle); + try { + var d = instance.web.str_to_date(instance.web.parse_value(needle, {'widget': 'date'})); + } catch (e) { + return false; + } if (!d) { return $.when(null); } var date_string = instance.web.format_value(d, this.attrs); var label = _.str.sprintf(_.str.escapeHTML( From ef55d19e3494e78b42c4d8f5a8968394b0713813 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 13 Nov 2014 11:26:44 +0100 Subject: [PATCH 09/52] [FIX] hr_timehseet: restrict access right Fix error while moving it at ca209eb --- addons/hr_timesheet/security/ir.model.access.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_timesheet/security/ir.model.access.csv b/addons/hr_timesheet/security/ir.model.access.csv index 4ac30f025ba..3673a3d5f64 100644 --- a/addons/hr_timesheet/security/ir.model.access.csv +++ b/addons/hr_timesheet/security/ir.model.access.csv @@ -10,4 +10,4 @@ access_product_uom_hr_timesheet,product.uom.hr.timesheet,product.model_product_u access_account_fiscalyear_hr_user,account.account.fiscalyear.user,account.model_account_fiscalyear,base.group_hr_user,1,1,1,1 access_hr_analytic_timesheet_user,hr.analytic.timesheet.user,model_hr_analytic_timesheet,base.group_user,1,1,1,0 access_account_analytic_journal_user,account.account.analytic.journal.user,account.model_account_analytic_journal,base.group_user,1,0,0,0 -access_hr_timesheet_report,access_hr_timesheet_report,model_hr_timesheet_report,,1,1,1,1 +access_hr_timesheet_report,hr.timesheet.report,model_hr_timesheet_report,base.group_hr_manager,1,1,0,0 From 15763ebf283953ebf0269e9101d6113918e9a380 Mon Sep 17 00:00:00 2001 From: grzegorz-openglobe Date: Wed, 24 Sep 2014 11:35:10 +0200 Subject: [PATCH 10/52] [IMP] l10n_pl: backport of fd86b7a to 8.0 [IMP] l10n_pl: some corrections in Polish CoA PLN as default currency Many improvements in taxes and fiscal positions Fixes #825 --- addons/l10n_pl/__openerp__.py | 6 +- addons/l10n_pl/account_chart.xml | 23 +- addons/l10n_pl/account_tax.xml | 573 +++++++++++++-------- addons/l10n_pl/country_pl.xml | 0 addons/l10n_pl/fiscal_position.xml | 53 +- addons/l10n_pl/static/description/icon.png | Bin 531 -> 698 bytes 6 files changed, 407 insertions(+), 248 deletions(-) mode change 100644 => 100755 addons/l10n_pl/country_pl.xml mode change 100644 => 100755 addons/l10n_pl/fiscal_position.xml diff --git a/addons/l10n_pl/__openerp__.py b/addons/l10n_pl/__openerp__.py index 9902e63d092..da66eec3184 100644 --- a/addons/l10n_pl/__openerp__.py +++ b/addons/l10n_pl/__openerp__.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2009 Grzegorz Grzelak grzegorz.grzelak@cirrus.pl +# Copyright (C) 2009 - now Grzegorz Grzelak grzegorz.grzelak@openglobe.pl # All Rights Reserved # $Id$ # @@ -22,7 +22,7 @@ ############################################################################## { 'name' : 'Poland - Accounting', - 'version' : '1.0', + 'version' : '1.02', 'author' : 'Grzegorz Grzelak (OpenGLOBE)', 'website': 'http://www.openglobe.pl', 'category' : 'Localization/Account Charts', @@ -35,7 +35,7 @@ rejestrów podatkowych. Moduł ustawia też konta do kupna i sprzedaży towarów zakładając, że wszystkie towary są w obrocie hurtowym. Niniejszy moduł jest przeznaczony dla odoo 8.0. -Wewnętrzny numer wersji OpenGLOBE 1.01 +Wewnętrzny numer wersji OpenGLOBE 1.02 """, 'depends' : ['account', 'base_iban', 'base_vat', 'account_chart'], 'demo' : [], diff --git a/addons/l10n_pl/account_chart.xml b/addons/l10n_pl/account_chart.xml index 368ceeb00b0..2937950e442 100755 --- a/addons/l10n_pl/account_chart.xml +++ b/addons/l10n_pl/account_chart.xml @@ -3204,17 +3204,18 @@ - - Polska - Plan kont - - - - - - - - - + + + Polska - Plan kont + + + + + + + + + diff --git a/addons/l10n_pl/account_tax.xml b/addons/l10n_pl/account_tax.xml index 1968cd28fce..ebf7a301181 100755 --- a/addons/l10n_pl/account_tax.xml +++ b/addons/l10n_pl/account_tax.xml @@ -2,7 +2,7 @@ -# VAT kraj należny (sprzedaż) +# VAT domestic sale @@ -11,7 +11,7 @@ 0.230000 percent sale - + @@ -21,6 +21,7 @@ + VAT-22% @@ -37,6 +38,7 @@ + VAT-8% @@ -53,6 +55,7 @@ + VAT-7% @@ -69,6 +72,7 @@ + VAT-5% @@ -85,6 +89,7 @@ + VAT-3% @@ -114,6 +119,7 @@ + VAT-ZW @@ -143,7 +149,6 @@ - VAT usł-22% @@ -161,8 +166,7 @@ - -# VAT kraj naliczony +# VAT domestic purchase @@ -170,7 +174,7 @@ Z23 0.230000 percent - + purchase @@ -181,6 +185,7 @@ + VAT naliczony-22% @@ -197,6 +202,7 @@ + VAT naliczony-8% @@ -213,6 +219,7 @@ + VAT naliczony-7% @@ -229,6 +236,7 @@ + VAT naliczony-5% @@ -245,6 +253,7 @@ + VAT naliczony-3% @@ -276,6 +285,7 @@ + VAT naliczony-ZW @@ -307,6 +317,7 @@ + VAT usł nalicz-22% @@ -324,223 +335,7 @@ - -# Unia -#=========================================================== - - - - Dost tow. unia - UDT - 0.000000 - percent - sale - - - - - - - - Nab tow unia - UNT - 0.23 - percent - - purchase - - - - - - - - Nab tow unia 1 - UNT1 - - - percent - - - - - - purchase - - - - Nab tow unia 2 - UNT2 - - - percent - - - - purchase - - - -# Eksport / Import -#================================================ - - - - Eksp Tow - EXT - 0.000000 - percent - sale - - - - - - - - Imp Tow - IMT - 0.23 - percent - - purchase - - - - - - - - Imp Tow 1 - IMT1 - - - percent - - - - - - purchase - - - - Imp Tow 2 - IMT2 - - - percent - - - - purchase - - - - - Eksp Usł - EXU - 0.000000 - percent - sale - - - - - - - - Świad Usł - UDU - 0.000000 - percent - sale - - - - - - - - Imp Usł - IMU - 0.23 - percent - - purchase - - - - - - - - Imp Usł 1 - IMU1 - - - percent - - - - - - purchase - - - - Imp Usł 2 - IMU2 - - - percent - - - - purchase - - - - - Nab Usł - UNU - 0.23 - percent - - purchase - - - - - - - - Nab Usł 1 - UNU1 - - - percent - - - - - - purchase - - - - Nab Usł 2 - UNU2 - - - percent - - - - purchase - - -# Leasing pojazdu +# Vehicle leasing #================================================ @@ -584,5 +379,337 @@ all +# Steel trade + + + + Sprzedaż stali + VST + 0.000000 + percent + sale + + + + + + + + Zakup stali + ZST + 1.0 + percent + + purchase + + + + + + Zakup stali - podatek naliczony + ZST1 + + + percent + + + + + + + + + + + + purchase + + + + + Zakup stali - podatek należny + ZST2 + + + percent + + + + + + + + + + + + purchase + + +# Eurpean Union +#=========================================================== + + + + Dost tow. unia + UDT + 0.000000 + percent + sale + + + + + + + + Nab tow unia + UNT + 1.0 + percent + + purchase + + + + + + Nab tow unia - podatek naliczony + UNT1 + + + percent + + + + + + + + + + + + purchase + + + + + Nab tow unia - podatek należny + UNT2 + + + percent + + + + + + + + + + + + purchase + + + + + Świad Usł + UDU + 0.000000 + percent + sale + + + + + + + + Nab Usł + UNU + 1.0 + percent + + purchase + + + + + Nab Usł - podatek naliczony + UNU1 + + + percent + + + + + + + + + + + + purchase + + + + + Nab Usł - podatek należny + UNU2 + + + percent + + + + + + + + + + + + purchase + + +# Export / Import +#================================================ + + + + Eksp Tow + EXT + 0.000000 + percent + sale + + + + + + + + Imp Tow + IMT + 1.0 + percent + + purchase + + + + + Imp Tow - podatek naliczony + IMT1 + + + percent + + + + + + + + + + + + purchase + + + + + Imp Tow - podatek należny + IMT2 + + + percent + + + + + + + + + + + + purchase + + + + + Eksp Usł + EXU + 0.000000 + percent + sale + + + + + + + + + Imp Usł + IMU + 1.00 + percent + + purchase + + + + + Imp Usł - podatek naliczony + IMU1 + + + percent + + + + + + + + + + + + purchase + + + + + Imp Usł - podatek należny + IMU2 + + + percent + + + + + + + + + + + + purchase + + + diff --git a/addons/l10n_pl/country_pl.xml b/addons/l10n_pl/country_pl.xml old mode 100644 new mode 100755 diff --git a/addons/l10n_pl/fiscal_position.xml b/addons/l10n_pl/fiscal_position.xml old mode 100644 new mode 100755 index 5dc0cbe953d..46627c60cd5 --- a/addons/l10n_pl/fiscal_position.xml +++ b/addons/l10n_pl/fiscal_position.xml @@ -21,96 +21,114 @@ - - + + + + + + + + + + + + -# Zakup + + + + + + + + + @@ -122,6 +140,7 @@ + @@ -129,47 +148,51 @@ -# Eksport / Import -#==================================================== - - -# Sprzedaż + + + + + + + + + @@ -181,51 +204,58 @@ + - -# Zakupy - + + + + + + + + + @@ -237,6 +267,7 @@ + diff --git a/addons/l10n_pl/static/description/icon.png b/addons/l10n_pl/static/description/icon.png index 7be06bbe5328b4dde28d735139e7df74c79eac90..0c376f25d9df4b58804b959c8654ec70946a861f 100644 GIT binary patch literal 698 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSEX7WqAsj$Z!;#X#z`#`K>Eakt zG3V_q-#jTtfukS8U75M$nfRVfexkB3ep;Ms=b9WI4r$J`2AyN(FD6F4_*>7eAz64kK+{mcywEpgyWMjqzARaFH7oSKPs_I3xuvq6w{px*oq1(-`f1SSn`?HRtFgPD zv~k6{V6Tf+d)KW~_FTVyz1Hu)aq{x=p{={tU4H&Nz0|q?xO4mAg}eGxWc%IkzW@Hp zDn34bSKRumrFzr5&pj_LoxAUSsny(7uRRXS>luA6+*78JjqgUjQ@a)U_u{ff}YY3MvK!vN(Ur?Tn+SQP5ndc^TUkk%9##@f~+!3~=qYV9Z$&rnlM?c#ip)zNS* zc7Y_&I475etqDvO(tM3JY_}c>WaLF~WZq?+^-%7>o$tX6F+f-Asiq1r9-F~%Z3nx- z^gVpv#H5xn_IVD8A3F=)(|h!+JxRY0XB#l)hGh z;vCl5$?^wevL8OHkj(+9Ss0*`{7GYKu!73k%{`Z{XsONWF_@Y*@t5JDLzidF&y^Iu zT6cYtNoa=PbP0l+XkK6S*A# literal 531 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSY)RhkEKEH~&`P=o!FXtcF@VTmfxw*lMJACO5Gq3kk(;k>vFu#H5hS|TEr8Rk` zh9;#t0J*9qt`Q|Ei6yC4$wjF^iowXh&`8(7MAyJ5#L&db*x1U*RNKJF%D`a#G>%#n z4Y~O#nQ4`18VoF~jEo@~?np}T0yRj2YzWRzD=AMbN@XZ7FW1Y=%Pvk%EJ)SMFG`>N S&PEETh{4m<&t;ucLK6U}aF&Gt From 86f785ae1b2741ab67384f3022b971ab638daeb8 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 5 Nov 2014 11:45:04 +0100 Subject: [PATCH 11/52] [FIX] #3153 Digits in wizard should be those of moves,... / Recreate delivery order from sale / Delivery method on delivery order [IMP] Provide better purchase order picking type [IMP] Possibly a better product uos handling in the sale order line [FIX] Recreate of delivery order when sales order in shipping exception [FIX] Delivery method should be passed to delivery order --- addons/delivery/sale.py | 6 +----- addons/delivery/stock.py | 9 +++++++++ addons/purchase/purchase.py | 14 +++++++++++++- addons/sale/sale.py | 3 ++- addons/sale_journal/__openerp__.py | 2 +- addons/sale_stock/sale_stock_view.xml | 14 ++++---------- addons/stock/wizard/stock_transfer_details.py | 2 +- 7 files changed, 31 insertions(+), 19 deletions(-) diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py index 4ef3224fc3e..a7e8b5d3716 100644 --- a/addons/delivery/sale.py +++ b/addons/delivery/sale.py @@ -53,10 +53,6 @@ class sale_order(osv.Model): result['value']['carrier_id'] = dtype return result - def _prepare_order_picking(self, cr, uid, order, context=None): - result = super(sale_order, self)._prepare_order_picking(cr, uid, order, context=context) - result.update(carrier_id=order.carrier_id.id) - return result def _delivery_unset(self, cr, uid, ids, context=None): sale_obj = self.pool['sale.order.line'] @@ -92,4 +88,4 @@ class sale_order(osv.Model): 'price_unit': grid_obj.get_price(cr, uid, grid.id, order, time.strftime('%Y-%m-%d'), context), 'tax_id': [(6, 0, taxes_ids)], 'is_delivery': True - }) + }) \ No newline at end of file diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py index 7bfd52df420..33f17f4e3dc 100644 --- a/addons/delivery/stock.py +++ b/addons/delivery/stock.py @@ -170,6 +170,15 @@ class stock_move(osv.osv): 'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",), } + def action_confirm(self, cr, uid, ids, context=None): + res = super(stock_move, self).action_confirm(cr, uid, ids, context=context) + pick_obj = self.pool.get("stock.picking") + for move in self.browse(cr, uid, ids, context=context): + if move.picking_id and move.procurement_id and move.procurement_id.sale_line_id and not move.picking_id.carrier_id : + pick_obj.write(cr, uid, [move.picking_id.id], {'carrier_id': move.procurement_id.sale_line_id.order_id.carrier_id.id}, context=context) + return res + + def _get_default_uom(self, cr, uid, context=None): uom_categ_id = self.pool.get('ir.model.data').xmlid_to_res_id(cr, uid, 'product.product_uom_categ_kgm') return self.pool.get('product.uom').search(cr, uid, [('category_id', '=', uom_categ_id),('factor','=',1)])[0] diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index b87061696e4..bec11639a86 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -149,8 +149,20 @@ class purchase_order(osv.osv): return res and res[0] or False def _get_picking_in(self, cr, uid, context=None): + obj_data = self.pool.get('ir.model.data') - return obj_data.get_object_reference(cr, uid, 'stock','picking_type_in') and obj_data.get_object_reference(cr, uid, 'stock','picking_type_in')[1] or False + type_obj = self.pool.get('stock.picking.type') + user_obj = self.pool.get('res.users') + company_id = user_obj.browse(cr, uid, uid, context=context).company_id.id + pick_type = obj_data.get_object_reference(cr, uid, 'stock', 'picking_type_in') and obj_data.get_object_reference(cr, uid, 'stock', 'picking_type_in')[1] or False + if pick_type: + type = type_obj.browse(cr, uid, pick_type, context=context) + if type and type.warehouse_id and type.warehouse_id.company_id.id == company_id: + return pick_type + types = type_obj.search(cr, uid, [('code', '=', 'incoming')], context=context) + if not types: + raise osv.except_osv(_('Error!'), _("Make sure you have at least an incoming picking type defined")) + return types[0] def _get_picking_ids(self, cr, uid, ids, field_names, args, context=None): res = {} diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 2273a889aaf..30cec292af8 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -712,10 +712,11 @@ class sale_order(osv.osv): line.refresh() #run again procurement that are in exception in order to trigger another move proc_ids += [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')] + procurement_obj.reset_to_confirmed(cr, uid, proc_ids, context=context) elif sale_line_obj.need_procurement(cr, uid, [line.id], context=context): if (line.state == 'done') or not line.product_id: continue - vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context) + vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context) proc_id = procurement_obj.create(cr, uid, vals, context=context) proc_ids.append(proc_id) #Confirm procurement order such that rules will be applied on it diff --git a/addons/sale_journal/__openerp__.py b/addons/sale_journal/__openerp__.py index 5c03c562033..643dc687441 100644 --- a/addons/sale_journal/__openerp__.py +++ b/addons/sale_journal/__openerp__.py @@ -59,7 +59,7 @@ Some statistics by journals are provided. ], 'demo': ['sale_journal_demo.xml'], 'test': [ ], - 'installable': True, + 'installable': False, 'auto_install': False, } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_stock/sale_stock_view.xml b/addons/sale_stock/sale_stock_view.xml index 0d5223a03c8..404316324f5 100644 --- a/addons/sale_stock/sale_stock_view.xml +++ b/addons/sale_stock/sale_stock_view.xml @@ -38,17 +38,11 @@ + on_change="product_id_change_with_wh(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,False,name,parent.partner_id, False, False, parent.date_order, product_packaging, parent.fiscal_position, True, parent.warehouse_id, context)"/> - - - - - + diff --git a/addons/stock/wizard/stock_transfer_details.py b/addons/stock/wizard/stock_transfer_details.py index 831f225ca13..17f302f6317 100644 --- a/addons/stock/wizard/stock_transfer_details.py +++ b/addons/stock/wizard/stock_transfer_details.py @@ -133,7 +133,7 @@ class stock_transfer_details_items(models.TransientModel): packop_id = fields.Many2one('stock.pack.operation', 'Operation') product_id = fields.Many2one('product.product', 'Product') product_uom_id = fields.Many2one('product.uom', 'Product Unit of Measure') - quantity = fields.Float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), default = 1.0) + quantity = fields.Float('Quantity', digits=dp.get_precision('Product Unit of Measure'), default = 1.0) package_id = fields.Many2one('stock.quant.package', 'Source package', domain="['|', ('location_id', 'child_of', sourceloc_id), ('location_id','=',False)]") lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number') sourceloc_id = fields.Many2one('stock.location', 'Source Location', required=True) From 6b9668520c66c7816d28cd74482e63db7599d5a8 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 6 Nov 2014 11:40:04 +0100 Subject: [PATCH 12/52] [FIX] Method should pass the correct objects for adding the line for the delivery method when invoicing based on shipments --- addons/delivery/stock.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py index 33f17f4e3dc..b62ecf58f09 100644 --- a/addons/delivery/stock.py +++ b/addons/delivery/stock.py @@ -120,9 +120,10 @@ class stock_picking(osv.osv): } def _create_invoice_from_picking(self, cr, uid, picking, vals, context=None): + invoice_obj = self.pool.get('account.invoice') invoice_line_obj = self.pool.get('account.invoice.line') invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context) - invoice = self.browse(cr, uid, invoice_id, context=context) + invoice = invoice_obj.browse(cr, uid, invoice_id, context=context) invoice_line = self._prepare_shipping_invoice_line(cr, uid, picking, invoice, context=context) if invoice_line: invoice_line_obj.create(cr, uid, invoice_line) From 518b2f6a3fbe87cde706c304a026e77999d0f9ef Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 4 Nov 2014 17:28:43 +0100 Subject: [PATCH 13/52] [FIX] super of scheduler / delivery method and phantom boms / average price [FIX] super of scheduler should have same params + use_new_cursor should be passed to procure orderpoint confirm [IMP] Make sure the delivery works when doing phantom boms [FIX] This should update the average price properly when having multiple moves with the same product [FIX] Average price should take into account the quantities of all variants [FIX] Make sure purchase picking type in other company works [IMP] Views of quants and destination locations of moves --- addons/delivery/stock.py | 15 ++++++++++++--- addons/purchase/purchase.py | 7 ++++--- addons/stock/procurement.py | 4 ++-- addons/stock/stock_view.xml | 4 ++-- addons/stock_account/stock_account.py | 12 ++++++++++-- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py index b62ecf58f09..c8781244fe0 100644 --- a/addons/delivery/stock.py +++ b/addons/delivery/stock.py @@ -172,11 +172,20 @@ class stock_move(osv.osv): } def action_confirm(self, cr, uid, ids, context=None): + """ + Pass the carrier to the picking from the sales order + (Should also work in case of Phantom BoMs when on explosion the original move is deleted) + """ + procs_to_check = [] + for move in self.browse(cr, uid, ids, context=context): + if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.carrier_id: + procs_to_check += [move.procurement_id] res = super(stock_move, self).action_confirm(cr, uid, ids, context=context) pick_obj = self.pool.get("stock.picking") - for move in self.browse(cr, uid, ids, context=context): - if move.picking_id and move.procurement_id and move.procurement_id.sale_line_id and not move.picking_id.carrier_id : - pick_obj.write(cr, uid, [move.picking_id.id], {'carrier_id': move.procurement_id.sale_line_id.order_id.carrier_id.id}, context=context) + for proc in procs_to_check: + pickings = list(set([x.picking_id.id for x in proc.move_ids if x.picking_id and not x.picking_id.carrier_id])) + if pickings: + pick_obj.write(cr, uid, pickings, {'carrier_id': proc.sale_line_id.order_id.carrier_id.id}, context=context) return res diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index bec11639a86..f08c16943b6 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -149,7 +149,6 @@ class purchase_order(osv.osv): return res and res[0] or False def _get_picking_in(self, cr, uid, context=None): - obj_data = self.pool.get('ir.model.data') type_obj = self.pool.get('stock.picking.type') user_obj = self.pool.get('res.users') @@ -159,9 +158,11 @@ class purchase_order(osv.osv): type = type_obj.browse(cr, uid, pick_type, context=context) if type and type.warehouse_id and type.warehouse_id.company_id.id == company_id: return pick_type - types = type_obj.search(cr, uid, [('code', '=', 'incoming')], context=context) + types = type_obj.search(cr, uid, [('code', '=', 'incoming'), ('warehouse_id.company_id', '=', company_id)], context=context) if not types: - raise osv.except_osv(_('Error!'), _("Make sure you have at least an incoming picking type defined")) + types = type_obj.search(cr, uid, [('code', '=', 'incoming'), ('warehouse_id', '=', False)], context=context) + if not types: + raise osv.except_osv(_('Error!'), _("Make sure you have at least an incoming picking type defined")) return types[0] def _get_picking_ids(self, cr, uid, ids, field_names, args, context=None): diff --git a/addons/stock/procurement.py b/addons/stock/procurement.py index 07024a2529e..c105ed6c017 100644 --- a/addons/stock/procurement.py +++ b/addons/stock/procurement.py @@ -273,7 +273,7 @@ class procurement_order(osv.osv): @param context: A standard dictionary for contextual values @return: Dictionary of values ''' - super(procurement_order, self).run_scheduler(cr, uid, use_new_cursor=use_new_cursor, context=context) + super(procurement_order, self).run_scheduler(cr, uid, use_new_cursor=use_new_cursor, company_id=company_id, context=context) if context is None: context = {} try: @@ -283,7 +283,7 @@ class procurement_order(osv.osv): move_obj = self.pool.get('stock.move') #Minimum stock rules - self._procure_orderpoint_confirm(cr, SUPERUSER_ID, use_new_cursor=False, company_id=company_id, context=context) + self._procure_orderpoint_confirm(cr, SUPERUSER_ID, use_new_cursor=use_new_cursor, company_id=company_id, context=context) #Search all confirmed stock_moves and try to assign them confirmed_ids = move_obj.search(cr, uid, [('state', '=', 'confirmed')], limit=None, order='priority desc, date_expected asc', context=context) diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 7e45cb20a8d..d6a6dcf01f8 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -1093,7 +1093,7 @@ - + @@ -1662,7 +1662,7 @@ stock.quant -
+
diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index c5308fde462..9707263391a 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -282,17 +282,25 @@ class stock_move(osv.osv): def product_price_update_before_done(self, cr, uid, ids, context=None): product_obj = self.pool.get('product.product') + tmpl_dict = {} for move in self.browse(cr, uid, ids, context=context): #adapt standard price on incomming moves if the product cost_method is 'average' if (move.location_id.usage == 'supplier') and (move.product_id.cost_method == 'average'): product = move.product_id - product_avail = product.qty_available - if product.qty_available <= 0: + prod_tmpl_id = move.product_id.product_tmpl_id.id + qty_available = move.product_id.product_tmpl_id.qty_available + if tmpl_dict.get(prod_tmpl_id): + product_avail = qty_available + tmpl_dict[prod_tmpl_id] + else: + tmpl_dict[prod_tmpl_id] = 0 + product_avail = qty_available + if product_avail <= 0: new_std_price = move.price_unit else: # Get the standard price amount_unit = product.standard_price new_std_price = ((amount_unit * product_avail) + (move.price_unit * move.product_qty)) / (product_avail + move.product_qty) + tmpl_dict[prod_tmpl_id] += move.product_qty # Write the standard price, as SUPERUSER_ID because a warehouse manager may not have the right to write on products product_obj.write(cr, SUPERUSER_ID, [product.id], {'standard_price': new_std_price}, context=context) From d667d60c45e28de97a764391a94f8c5782865456 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 24 Oct 2014 17:50:31 +0200 Subject: [PATCH 14/52] [IMP] When an inventory has lines already, don't delete them, that way we can import them [IMP] Put the theoretical quantity when importing an inventory --- addons/stock/stock.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index ab0807b40bf..dc7681f505c 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2641,12 +2641,29 @@ class stock_inventory(osv.osv): inventory_line_obj = self.pool.get('stock.inventory.line') for inventory in self.browse(cr, uid, ids, context=context): #clean the existing inventory lines before redoing an inventory proposal + # INSTEAD: if there are already inventory lines, respect those line_ids = [line.id for line in inventory.line_ids] - inventory_line_obj.unlink(cr, uid, line_ids, context=context) - #compute the inventory lines and create them - vals = self._get_inventory_lines(cr, uid, inventory, context=context) - for product_line in vals: - inventory_line_obj.create(cr, uid, product_line, context=context) + if not line_ids: + #compute the inventory lines and create them + vals = self._get_inventory_lines(cr, uid, inventory, context=context) + for product_line in vals: + inventory_line_obj.create(cr, uid, product_line, context=context) + else: + # On import calculate theoretical quantity + quant_obj = self.pool.get("stock.quant") + for line in inventory.line_ids: + dom = [('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id), + ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id)] + if line.package_id: + dom += [('package_id', '=', line.package_id.id)] + if line.company_id: + dom += [('company_id', '=', line.company_id.id)] + quants = quant_obj.search(cr, uid, dom, context=context) + tot_qty = 0 + for quant in quant_obj.browse(cr, uid, quants, context=context): + tot_qty += quant.qty + inventory_line_obj.write(cr, uid, [line.id],{'theoretical_qty': tot_qty}, context=context) + return self.write(cr, uid, ids, {'state': 'confirm', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) def _get_inventory_lines(self, cr, uid, inventory, context=None): @@ -2712,7 +2729,7 @@ class stock_inventory_line(osv.osv): 'company_id': fields.related('inventory_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, select=True, readonly=True), 'prod_lot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"), 'state': fields.related('inventory_id', 'state', type='char', string='Status', readonly=True), - 'theoretical_qty': fields.float('Theoretical Quantity', readonly=True), + 'theoretical_qty': fields.float('Theoretical Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), readonly=True), 'partner_id': fields.many2one('res.partner', 'Owner'), 'product_name': fields.related('product_id', 'name', type='char', string='Product Name', store={ 'product.product': (_get_product_name_change, ['name', 'default_code'], 20), From 94b768d30f3f5c390bbaab4aac5e2f312ed171ca Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 13 Nov 2014 11:49:27 +0100 Subject: [PATCH 15/52] [IMP] Update comments for inventory from import --- addons/stock/stock.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index dc7681f505c..902a9f287e9 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2640,8 +2640,7 @@ class stock_inventory(osv.osv): def prepare_inventory(self, cr, uid, ids, context=None): inventory_line_obj = self.pool.get('stock.inventory.line') for inventory in self.browse(cr, uid, ids, context=context): - #clean the existing inventory lines before redoing an inventory proposal - # INSTEAD: if there are already inventory lines, respect those + # If there are inventory lines already (e.g. from import), respect those and set their theoretical qty line_ids = [line.id for line in inventory.line_ids] if not line_ids: #compute the inventory lines and create them @@ -2652,17 +2651,15 @@ class stock_inventory(osv.osv): # On import calculate theoretical quantity quant_obj = self.pool.get("stock.quant") for line in inventory.line_ids: - dom = [('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id), + dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id), ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id)] if line.package_id: dom += [('package_id', '=', line.package_id.id)] - if line.company_id: - dom += [('company_id', '=', line.company_id.id)] quants = quant_obj.search(cr, uid, dom, context=context) tot_qty = 0 for quant in quant_obj.browse(cr, uid, quants, context=context): tot_qty += quant.qty - inventory_line_obj.write(cr, uid, [line.id],{'theoretical_qty': tot_qty}, context=context) + inventory_line_obj.write(cr, uid, [line.id], {'theoretical_qty': tot_qty}, context=context) return self.write(cr, uid, ids, {'state': 'confirm', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) From 5d2ea601570121b0db068ddf3dd6521c4abe0843 Mon Sep 17 00:00:00 2001 From: Frederic van der Essen Date: Thu, 13 Nov 2014 12:03:11 +0100 Subject: [PATCH 16/52] [FIX] point_of_sale: I see a chrome receipt and I want it painted black ;) --- addons/point_of_sale/static/src/css/pos.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index 036f7a73eda..db792954304 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -1118,6 +1118,9 @@ td { } @media print { + * { + color: black !important; + } .oe_leftbar, .pos .pos-topheader, .pos .pos-leftpane, From b8071bbacafe0eadb353329f7e213241b9b96bdf Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 13 Nov 2014 12:30:26 +0100 Subject: [PATCH 17/52] [FIX] addons: incorrect new-api invocation of method write() with context This fixes #3411. --- addons/crm/crm_lead.py | 2 +- addons/google_calendar/google_calendar.py | 6 +++--- addons/hr_recruitment/hr_recruitment.py | 2 +- addons/mrp/mrp.py | 2 +- addons/product/product.py | 6 +++--- addons/sale/sale.py | 2 +- addons/website_sale_delivery/models/sale_order.py | 8 ++++---- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 866680eb766..992e4f365ea 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -793,7 +793,7 @@ class crm_lead(format_address, osv.osv): partner_id = self._create_lead_partner(cr, uid, lead, context) self.pool['res.partner'].write(cr, uid, partner_id, {'section_id': lead.section_id and lead.section_id.id or False}) if partner_id: - lead.write({'partner_id': partner_id}, context=context) + lead.write({'partner_id': partner_id}) partner_ids[lead.id] = partner_id return partner_ids diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py index 03a311dd2ff..3d1a87661d2 100644 --- a/addons/google_calendar/google_calendar.py +++ b/addons/google_calendar/google_calendar.py @@ -494,7 +494,7 @@ class google_calendar(osv.AbstractModel): all_my_attendees = self.pool['calendar.attendee'].search(cr, uid, [('partner_id', '=', current_user.partner_id.id)], context=context) self.pool['calendar.attendee'].write(cr, uid, all_my_attendees, {'oe_synchro_date': False, 'google_internal_event_id': False}, context=context) - current_user.write(reset_data, context=context) + current_user.write(reset_data) return True def synchronize_events_cron(self, cr, uid, context=None): @@ -552,7 +552,7 @@ class google_calendar(osv.AbstractModel): lastSync = False _logger.info("[%s] Calendar Synchro - MODE FULL SYNCHRO FORCED" % user_to_sync) else: - current_user.write({'google_calendar_cal_id': current_google}, context=context) + current_user.write({'google_calendar_cal_id': current_google}) lastSync = False _logger.info("[%s] Calendar Synchro - MODE FULL SYNCHRO - NEW CAL ID" % user_to_sync) @@ -562,7 +562,7 @@ class google_calendar(osv.AbstractModel): res = self.update_events(cr, uid, lastSync, context) - current_user.write({'google_calendar_last_sync_date': ask_time}, context=context) + current_user.write({'google_calendar_last_sync_date': ask_time}) return { "status": res and "need_refresh" or "no_new_event_form_google", "url": '' diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 38777eb7907..911c0194c14 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -488,7 +488,7 @@ class hr_applicant(osv.Model): address_id = self.pool.get('res.partner').address_get(cr, uid, [applicant.partner_id.id], ['contact'])['contact'] contact_name = self.pool.get('res.partner').name_get(cr, uid, [applicant.partner_id.id])[0][1] if applicant.job_id and (applicant.partner_name or contact_name): - applicant.job_id.write({'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1}, context=context) + applicant.job_id.write({'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1}) create_ctx = dict(context, mail_broadcast=True) emp_id = hr_employee.create(cr, uid, {'name': applicant.partner_name or contact_name, 'job_id': applicant.job_id.id, diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 2a597336a78..2f8d3996c43 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -1184,7 +1184,7 @@ class mrp_production(osv.osv): self._make_service_procurement(cr, uid, line, context=context) if stock_moves: self.pool.get('stock.move').action_confirm(cr, uid, stock_moves, context=context) - production.write({'state': 'confirmed'}, context=context) + production.write({'state': 'confirmed'}) return 0 def action_assign(self, cr, uid, ids, context=None): diff --git a/addons/product/product.py b/addons/product/product.py index 7060a4f3992..4a94c00f6bb 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -854,7 +854,7 @@ class product_product(osv.osv): context['uom'], value, uom.id) value = value - product.price_extra - return product.write({'list_price': value}, context=context) + return product.write({'list_price': value}) def _get_partner_code_name(self, cr, uid, ids, product, partner_id, context=None): for supinfo in product.seller_ids: @@ -905,8 +905,8 @@ class product_product(osv.osv): res = self.write(cr, uid, [id], {'image_variant': image}, context=context) product = self.browse(cr, uid, id, context=context) if not product.product_tmpl_id.image: - product.write({'image_variant': None}, context=context) - product.product_tmpl_id.write({'image': image}, context=context) + product.write({'image_variant': None}) + product.product_tmpl_id.write({'image': image}) return res def _get_price_extra(self, cr, uid, ids, name, args, context=None): diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 30cec292af8..d1736a22cf0 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -702,7 +702,7 @@ class sale_order(osv.osv): vals = self._prepare_procurement_group(cr, uid, order, context=context) if not order.procurement_group_id: group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context) - order.write({'procurement_group_id': group_id}, context=context) + order.write({'procurement_group_id': group_id}) for line in order.order_line: #Try to fix exception procurement (possible when after a shipping exception the user choose to recreate) diff --git a/addons/website_sale_delivery/models/sale_order.py b/addons/website_sale_delivery/models/sale_order.py index 6e71d3fa574..2fbe081d768 100644 --- a/addons/website_sale_delivery/models/sale_order.py +++ b/addons/website_sale_delivery/models/sale_order.py @@ -63,7 +63,7 @@ class SaleOrder(orm.Model): if not order: return False if all(line.product_id.type == "service" for line in order.website_order_line): - order.write({'carrier_id': None}, context=context) + order.write({'carrier_id': None}) self.pool['sale.order']._delivery_unset(cr, SUPERUSER_ID, [order.id], context=context) return True else: @@ -81,11 +81,11 @@ class SaleOrder(orm.Model): if grid_id: carrier_id = delivery_id break - order.write({'carrier_id': carrier_id}, context=context) + order.write({'carrier_id': carrier_id}) if carrier_id: - order.delivery_set(context=context) + order.delivery_set() else: - order._delivery_unset(context=context) + order._delivery_unset() return bool(carrier_id) From 2e604c9d08e472003867df1d210b338739db1769 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Wed, 12 Nov 2014 17:30:45 +0100 Subject: [PATCH 18/52] [FIX] mail_group: use default picture disc if not mimetype has been defined (else tb) --- addons/website_mail_group/views/website_mail_group.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_mail_group/views/website_mail_group.xml b/addons/website_mail_group/views/website_mail_group.xml index b72f5f5c903..b8172e4015d 100644 --- a/addons/website_mail_group/views/website_mail_group.xml +++ b/addons/website_mail_group/views/website_mail_group.xml @@ -212,7 +212,7 @@ t-att-title="attachment.name"/> - From d978c7824614a22048c140ffaa6b0256c6405542 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Thu, 13 Nov 2014 09:21:46 +0100 Subject: [PATCH 19/52] [FIX] website_sale: browse category to slugify. Because slug of an id will generate a tb 'need more than 1 value to unpack' --- addons/website_sale/controllers/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 1ea72c74645..6a27e9728da 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -182,6 +182,7 @@ class website_sale(http.Controller): if search: post["search"] = search if category: + category = pool['product.public.category'].browse(cr, uid, int(category), context=context) url = "/shop/category/%s" % slug(category) pager = request.website.pager(url=url, total=product_count, page=page, step=PPG, scope=7, url_args=post) product_ids = product_obj.search(cr, uid, domain, limit=PPG, offset=pager['offset'], order='website_published desc, website_sequence desc', context=context) From e3580ab884902b58349dcfc2377de1939fc7ffe2 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 13 Nov 2014 12:42:18 +0100 Subject: [PATCH 20/52] [FIX] account_voucher: getting default invoice crashes Fixes #3524. --- addons/account_voucher/account_voucher.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index e1483ab0188..78060e8a733 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -125,6 +125,9 @@ class account_voucher(osv.osv): journal_pool = self.pool.get('account.journal') journal_id = context.get('journal_id', False) if journal_id: + if isinstance(journal_id, (list, tuple)): + # sometimes journal_id is a pair (id, display_name) + journal_id = journal_id[0] journal = journal_pool.browse(cr, uid, journal_id, context=context) if journal.currency: return journal.currency.id From 3c203703dba05b0d0806d195234ba04adaf41c7e Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 6 Nov 2014 15:16:02 +0100 Subject: [PATCH 21/52] [FIX] ir_model: always setup models before calling _auto_init() --- openerp/addons/base/ir/ir_model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index e9a05abbfe4..307b32516a3 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -501,6 +501,7 @@ class ir_model_fields(osv.osv): # we want to change the key of field in obj._fields and obj._columns field = obj._pop_field(rename[1]) obj._add_field(rename[2], field) + self.pool.setup_models(cr, partial=(not self.pool.ready)) if patches: # We have to update _columns of the model(s) and then call their @@ -521,11 +522,11 @@ class ir_model_fields(osv.osv): obj._add_field(field_name, field.new(**attrs)) # update database schema + self.pool.setup_models(cr, partial=(not self.pool.ready)) obj._auto_init(cr, ctx) obj._auto_end(cr, ctx) # actually create FKs! if column_rename or patches: - self.pool.setup_models(cr, partial=(not self.pool.ready)) RegistryManager.signal_registry_change(cr.dbname) return res From fc2975a9afd04be227b21235d0acd3d493713072 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 6 Nov 2014 15:48:20 +0100 Subject: [PATCH 22/52] [IMP] models: in _init_manual_fields(), create fields instead of columns Note that serialized fields are ignored; they are no longer supported, anyway. --- openerp/models.py | 49 ++++++++++++++++++------------------------- openerp/osv/fields.py | 5 ++--- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/openerp/models.py b/openerp/models.py index 0dd16a69d1c..e871dfff2cc 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -709,41 +709,37 @@ class BaseModel(object): manual_fields = cr.dictfetchall() for field in manual_fields: - if field['name'] in cls._columns: + if field['name'] in cls._fields: continue attrs = { + 'manual': True, 'string': field['field_description'], 'required': bool(field['required']), 'readonly': bool(field['readonly']), - 'domain': eval(field['domain']) if field['domain'] else None, - 'size': field['size'] or None, - 'ondelete': field['on_delete'], - 'translate': (field['translate']), - 'manual': True, - '_prefetch': False, - #'select': int(field['select_level']) } - if field['serialization_field_id']: - cr.execute('SELECT name FROM ir_model_fields WHERE id=%s', (field['serialization_field_id'],)) - attrs.update({'serialization_field': cr.fetchone()[0], 'type': field['ttype']}) - if field['ttype'] in ['many2one', 'one2many', 'many2many']: - attrs.update({'relation': field['relation']}) - cls._columns[field['name']] = fields.sparse(**attrs) - elif field['ttype'] == 'selection': - cls._columns[field['name']] = fields.selection(eval(field['selection']), **attrs) - elif field['ttype'] == 'reference': - cls._columns[field['name']] = fields.reference(selection=eval(field['selection']), **attrs) + # FIXME: ignore field['serialization_field_id'] + if field['ttype'] in ('char', 'text', 'html'): + attrs['translate'] = bool(field['translate']) + attrs['size'] = field['size'] or None + elif field['ttype'] in ('selection', 'reference'): + attrs['selection'] = eval(field['selection']) elif field['ttype'] == 'many2one': - cls._columns[field['name']] = fields.many2one(field['relation'], **attrs) + attrs['comodel_name'] = field['relation'] + attrs['ondelete'] = field['on_delete'] + attrs['domain'] = eval(field['domain']) if field['domain'] else None elif field['ttype'] == 'one2many': - cls._columns[field['name']] = fields.one2many(field['relation'], field['relation_field'], **attrs) + attrs['comodel_name'] = field['relation'] + attrs['inverse_name'] = field['relation_field'] + attrs['domain'] = eval(field['domain']) if field['domain'] else None elif field['ttype'] == 'many2many': + attrs['comodel_name'] = field['relation'] _rel1 = field['relation'].replace('.', '_') _rel2 = field['model'].replace('.', '_') - _rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name']) - cls._columns[field['name']] = fields.many2many(field['relation'], _rel_name, 'id1', 'id2', **attrs) - else: - cls._columns[field['name']] = getattr(fields, field['ttype'])(**attrs) + attrs['relation'] = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name']) + attrs['column1'] = 'id1' + attrs['column2'] = 'id2' + attrs['domain'] = eval(field['domain']) if field['domain'] else None + cls._add_field(field['name'], Field.by_type[field['ttype']](**attrs)) @classmethod def _init_constraints_onchanges(cls): @@ -2913,11 +2909,6 @@ class BaseModel(object): cls._inherit_fields = res cls._all_columns = cls._get_column_infos() - # interface columns with new-style fields - for attr, column in cls._columns.items(): - if attr not in cls._fields: - cls._add_field(attr, column.to_field()) - # interface inherited fields with new-style fields (note that the # reverse order is for being consistent with _all_columns above) for parent_model, parent_field in reversed(cls._inherits.items()): diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index e8d416652bb..59e607b4510 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -136,9 +136,8 @@ class _column(object): for key, val in args.iteritems(): setattr(self, key, val) - # prefetch only if self._classic_write, not self.groups, and not - # self.deprecated - if not self._classic_write or self.deprecated: + # prefetch only if _classic_write, not deprecated and not manual + if not self._classic_write or self.deprecated or self.manual: self._prefetch = False def new(self, **args): From 552dab0dc858b2d36e6d7a90bbd46dfdb7d9a821 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 13 Nov 2014 10:06:30 +0100 Subject: [PATCH 23/52] [FIX] models: simplify conditions that seem to lead to missing fields --- openerp/models.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openerp/models.py b/openerp/models.py index e871dfff2cc..02be95568f5 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -516,7 +516,7 @@ class BaseModel(object): """ def add(name, field): """ add `field` with the given `name` if it does not exist yet """ - if name not in cls._columns and name not in cls._fields: + if name not in cls._fields: cls._add_field(name, field) # cyclic import @@ -811,8 +811,7 @@ class BaseModel(object): cls._fields = {} above = cls.__bases__[0] for attr, field in getmembers(above, Field.__instancecheck__): - if not field.inherited: - cls._add_field(attr, field.new()) + cls._add_field(attr, field.new()) # introduce magic fields cls._add_magic_fields() From e038fec696ea7029b2f66da36fdbf337c23a18d1 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 6 Nov 2014 15:03:00 +0100 Subject: [PATCH 24/52] [IMP] models: improve performance of _setup_fields() There was an issue in _setup_fields(): the method invokes _inherits_reload(), which recomputes inherited fields, and invokes itself recursively on children models. This may be problematic if the children models have already been set up. This optimization avoids recursive calls of method _inherits_reload(). In _setup_fields(), first all parent models are set up, then their fields are inspected to determine inherited fields, and their setup is done. This scheme guarantees that inherited fields are computed once per model. --- openerp/fields.py | 10 ++- openerp/models.py | 151 +++++++++++++++++++----------------- openerp/modules/registry.py | 3 +- 3 files changed, 86 insertions(+), 78 deletions(-) diff --git a/openerp/fields.py b/openerp/fields.py index 58838bf0531..43b63f84b13 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -426,6 +426,7 @@ class Field(object): # put invalidation triggers on model dependencies for dep_model_name, field_names in model._depends.iteritems(): dep_model = env[dep_model_name] + dep_model._setup_fields() for field_name in field_names: field = dep_model._fields[field_name] field._triggers.add((self, None)) @@ -444,8 +445,8 @@ class Field(object): recs = env[self.model_name] fields = [] for name in self.related: + recs._setup_fields() field = recs._fields[name] - field.setup(env) recs = recs[name] fields.append(field) @@ -551,6 +552,7 @@ class Field(object): env = model.env head, tail = path1[0], path1[1:] + model._setup_fields() if head == '*': # special case: add triggers on all fields of model (except self) fields = set(model._fields.itervalues()) - set([self]) @@ -563,8 +565,6 @@ class Field(object): self.recursive = True continue - field.setup(env) - #_logger.debug("Add trigger on %s to recompute %s", field, self) field._triggers.add((self, '.'.join(path0 or ['id']))) @@ -1648,7 +1648,9 @@ class One2many(_RelationalMulti): if self.inverse_name: # link self to its inverse field and vice-versa - invf = env[self.comodel_name]._fields[self.inverse_name] + comodel = env[self.comodel_name] + comodel._setup_fields() + invf = comodel._fields[self.inverse_name] # In some rare cases, a `One2many` field can link to `Int` field # (res_model/res_id pattern). Only inverse the field if this is # a `Many2one` field. diff --git a/openerp/models.py b/openerp/models.py index 02be95568f5..df46fce4d5d 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -700,10 +700,10 @@ class BaseModel(object): pool._store_function[model].sort(key=lambda x: x[4]) @classmethod - def _init_manual_fields(cls, pool, cr): + def _init_manual_fields(cls, cr): # Check whether the query is already done - if pool.fields_by_model is not None: - manual_fields = pool.fields_by_model.get(cls._name, []) + if cls.pool.fields_by_model is not None: + manual_fields = cls.pool.fields_by_model.get(cls._name, []) else: cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (cls._name, 'manual')) manual_fields = cr.dictfetchall() @@ -752,12 +752,8 @@ class BaseModel(object): cls._onchange_methods = defaultdict(list) for attr, func in getmembers(cls, callable): if hasattr(func, '_constrains'): - if not all(name in cls._fields for name in func._constrains): - _logger.warning("@constrains%r parameters must be field names", func._constrains) cls._constraint_methods.append(func) if hasattr(func, '_onchange'): - if not all(name in cls._fields for name in func._onchange): - _logger.warning("@onchange%r parameters must be field names", func._onchange) for name in func._onchange: cls._onchange_methods[name].append(func) @@ -818,31 +814,10 @@ class BaseModel(object): # register stuff about low-level function fields and custom fields cls._init_function_fields(pool, cr) - cls._init_manual_fields(pool, cr) - - # process _inherits - cls._inherits_check() - cls._inherits_reload() # register constraints and onchange methods cls._init_constraints_onchanges() - # check defaults - for k in cls._defaults: - assert k in cls._fields, \ - "Model %s has a default for nonexiting field %s" % (cls._name, k) - - # restart columns - for column in cls._columns.itervalues(): - column.restart() - - # validate rec_name - if cls._rec_name: - assert cls._rec_name in cls._fields, \ - "Invalid rec_name %s for model %s" % (cls._rec_name, cls._name) - elif 'name' in cls._fields: - cls._rec_name = 'name' - # prepare ormcache, which must be shared by all instances of the model cls._ormcache = {} @@ -2884,42 +2859,34 @@ class BaseModel(object): # Update objects that uses this one to update their _inherits fields # - @classmethod - def _inherits_reload_src(cls): - """ Recompute the _inherit_fields mapping on each _inherits'd child model.""" - for model in cls.pool.values(): - if cls._name in model._inherits: - model._inherits_reload() - @classmethod def _inherits_reload(cls): - """ Recompute the _inherit_fields mapping. + """ Recompute the _inherit_fields mapping, and inherited fields. """ + struct = {} + fields = {} + for parent_model, parent_field in cls._inherits.iteritems(): + parent = cls.pool[parent_model] + # old-api struct for _inherit_fields + for name, column in parent._columns.iteritems(): + struct[name] = (parent_model, parent_field, column, parent_model) + for name, source in parent._inherit_fields.iteritems(): + struct[name] = (parent_model, parent_field, source[2], source[3]) + # new-api fields for _fields + for name, field in parent._fields.iteritems(): + fields[name] = field.new( + inherited=True, + related=(parent_field, name), + related_sudo=False, + ) - This will also call itself on each inherits'd child model. - - """ - res = {} - for table in cls._inherits: - other = cls.pool[table] - for col in other._columns.keys(): - res[col] = (table, cls._inherits[table], other._columns[col], table) - for col in other._inherit_fields.keys(): - res[col] = (table, cls._inherits[table], other._inherit_fields[col][2], other._inherit_fields[col][3]) - cls._inherit_fields = res + # old-api stuff + cls._inherit_fields = struct cls._all_columns = cls._get_column_infos() - # interface inherited fields with new-style fields (note that the - # reverse order is for being consistent with _all_columns above) - for parent_model, parent_field in reversed(cls._inherits.items()): - for attr, field in cls.pool[parent_model]._fields.iteritems(): - if attr not in cls._fields: - cls._add_field(attr, field.new( - inherited=True, - related=(parent_field, attr), - related_sudo=False, - )) - - cls._inherits_reload_src() + # add inherited fields that are not redefined locally + for name, field in fields.iteritems(): + if name not in cls._fields: + cls._add_field(name, field) @classmethod def _get_column_infos(cls): @@ -2959,34 +2926,72 @@ class BaseModel(object): @api.model def _prepare_setup_fields(self): """ Prepare the setup of fields once the models have been loaded. """ - for field in self._fields.itervalues(): - field.reset() + type(self)._setup_done = False + for name, field in self._fields.items(): + if field.inherited: + del self._fields[name] + else: + field.reset() @api.model - def _setup_fields(self, partial=False): + def _setup_fields(self): """ Setup the fields (dependency triggers, etc). """ - for field in self._fields.itervalues(): - try: - field.setup(self.env) - except Exception: - if not partial: - raise + cls = type(self) + if cls._setup_done: + return + cls._setup_done = True - # update columns (fields may have changed), and column_infos - for name, field in self._fields.iteritems(): + # first make sure that parent models are all set up + for parent in self._inherits: + self.env[parent]._setup_fields() + + # retrieve custom fields + if not self._context.get('_setup_fields_partial'): + cls._init_manual_fields(self._cr) + + # retrieve inherited fields + cls._inherits_check() + cls._inherits_reload() + + # set up fields + for field in cls._fields.itervalues(): + field.setup(self.env) + + # update columns (fields may have changed) + for name, field in cls._fields.iteritems(): if field.column: - self._columns[name] = field.to_column() - self._inherits_reload() + cls._columns[name] = field.to_column() # group fields by compute to determine field.computed_fields fields_by_compute = defaultdict(list) - for field in self._fields.itervalues(): + for field in cls._fields.itervalues(): if field.compute: field.computed_fields = fields_by_compute[field.compute] field.computed_fields.append(field) else: field.computed_fields = [] + # check constraints + for func in cls._constraint_methods: + if not all(name in cls._fields for name in func._constrains): + _logger.warning("@constrains%r parameters must be field names", func._constrains) + for name in cls._onchange_methods: + if name not in cls._fields: + func = cls._onchange_methods[name] + _logger.warning("@onchange%r parameters must be field names", func._onchange) + + # check defaults + for name in cls._defaults: + assert name in cls._fields, \ + "Model %s has a default for nonexiting field %s" % (cls._name, name) + + # validate rec_name + if cls._rec_name: + assert cls._rec_name in cls._fields, \ + "Invalid rec_name %s for model %s" % (cls._rec_name, cls._name) + elif 'name' in cls._fields: + cls._rec_name = 'name' + def fields_get(self, cr, user, allfields=None, context=None, write_access=True): """ fields_get([fields]) diff --git a/openerp/modules/registry.py b/openerp/modules/registry.py index 34672c44a78..7cf734d792e 100644 --- a/openerp/modules/registry.py +++ b/openerp/modules/registry.py @@ -164,8 +164,9 @@ class Registry(Mapping): # do the actual setup from a clean state self._m2m = {} + context = {'_setup_fields_partial': partial} for model in self.models.itervalues(): - model._setup_fields(cr, SUPERUSER_ID, partial=partial) + model._setup_fields(cr, SUPERUSER_ID, context=context) def clear_caches(self): """ Clear the caches From e826e84af5eeca402c6cb60bc53fa20d1de67ded Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 13 Nov 2014 16:02:11 +0100 Subject: [PATCH 25/52] [FIX] fleet: avoid function field computation order issues The name of a vehicle is function field. The field odometer on a vehicle has a a setter that creates an odometer. The name of an odometer is a function field that uses the name of the vehicle. If a vehicle is created with a value in odometer field, the odometer is created before the name is computed so the concatanation would fail. Fixes #3468 --- addons/fleet/fleet.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/fleet/fleet.py b/addons/fleet/fleet.py index 3683e79aba8..69900a68b85 100644 --- a/addons/fleet/fleet.py +++ b/addons/fleet/fleet.py @@ -426,8 +426,10 @@ class fleet_vehicle_odometer(osv.Model): res = {} for record in self.browse(cr, uid, ids, context=context): name = record.vehicle_id.name - if record.date: - name = name+ ' / '+ str(record.date) + if not name: + name = record.date + elif record.date: + name += ' / '+ record.date res[record.id] = name return res From d30103c29e82375fdee2810d822d628ba26358f9 Mon Sep 17 00:00:00 2001 From: Frederic van der Essen Date: Thu, 13 Nov 2014 16:07:52 +0100 Subject: [PATCH 26/52] [FIX] point_of_sale: rounding error in discount computing + removing duplicated code --- addons/point_of_sale/static/src/js/models.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 88d17b5d2bb..48b32d4a99e 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -810,10 +810,13 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal get_unit_price: function(){ return this.price; }, - get_display_price: function(){ + get_base_price: function(){ var rounding = this.pos.currency.rounding; return round_pr(round_pr(this.get_unit_price() * this.get_quantity(),rounding) * (1- this.get_discount()/100.0),rounding); }, + get_display_price: function(){ + return this.get_base_price(); + }, get_price_without_tax: function(){ return this.get_all_prices().priceWithoutTax; }, @@ -829,7 +832,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal get_all_prices: function(){ var self = this; var currency_rounding = this.pos.currency.rounding; - var base = round_pr(this.get_quantity() * this.get_unit_price() * (1.0 - (this.get_discount() / 100.0)), currency_rounding); + var base = this.get_base_price(); var totalTax = base; var totalNoTax = base; From 1a826e07fe61b8e55291f207da81eddf55baf14b Mon Sep 17 00:00:00 2001 From: Samus CTO Date: Thu, 13 Nov 2014 16:23:08 +0100 Subject: [PATCH 27/52] [IMP] Speedup test account_assert_test.xml in account --- addons/account/account.py | 13 +++++++++++++ addons/account/account_assert_test.xml | 4 +--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index a12b5bd1052..c1aca3ee8ec 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1157,6 +1157,19 @@ class account_move(osv.osv): _description = "Account Entry" _order = 'id desc' + def account_assert_balanced(self, cr, uid, context=None): + cr.execute("""\ + SELECT move_id + FROM account_move_line + WHERE state = 'valid' + GROUP BY move_id + HAVING abs(sum(debit) - sum(credit)) > 0.00001 + """) + assert len(cr.fetchall()) == 0, \ + "For all Journal Items, the state is valid implies that the sum " \ + "of credits equals the sum of debits" + return True + def account_move_prepare(self, cr, uid, journal_id, date=False, ref='', company_id=False, context=None): ''' Prepares and returns a dictionary of values, ready to be passed to create() based on the parameters received. diff --git a/addons/account/account_assert_test.xml b/addons/account/account_assert_test.xml index 88025514763..32e8afb1b50 100644 --- a/addons/account/account_assert_test.xml +++ b/addons/account/account_assert_test.xml @@ -1,8 +1,6 @@ - - - + From 911fa4a3f2b0f04ef70d93a438a625656719ab2a Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 13 Nov 2014 15:24:31 +0100 Subject: [PATCH 28/52] [IMP] Pass the invoice type from sale to picking for the sale_journal module that is installable again --- addons/sale_journal/__openerp__.py | 2 +- addons/sale_journal/sale_journal.py | 26 +++++++++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/addons/sale_journal/__openerp__.py b/addons/sale_journal/__openerp__.py index 643dc687441..5c03c562033 100644 --- a/addons/sale_journal/__openerp__.py +++ b/addons/sale_journal/__openerp__.py @@ -59,7 +59,7 @@ Some statistics by journals are provided. ], 'demo': ['sale_journal_demo.xml'], 'test': [ ], - 'installable': False, + 'installable': True, 'auto_install': False, } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_journal/sale_journal.py b/addons/sale_journal/sale_journal.py index 076692cbf4a..f6e4abb6b53 100644 --- a/addons/sale_journal/sale_journal.py +++ b/addons/sale_journal/sale_journal.py @@ -61,17 +61,33 @@ class picking(osv.osv): } +class stock_move(osv.osv): + _inherit = "stock.move" + + def action_confirm(self, cr, uid, ids, context=None): + """ + Pass the invoice type to the picking from the sales order + (Should also work in case of Phantom BoMs when on explosion the original move is deleted, similar to carrier_id on delivery) + """ + procs_to_check = [] + for move in self.browse(cr, uid, ids, context=context): + if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.invoice_type_id: + procs_to_check += [move.procurement_id] + res = super(stock_move, self).action_confirm(cr, uid, ids, context=context) + pick_obj = self.pool.get("stock.picking") + for proc in procs_to_check: + pickings = list(set([x.picking_id.id for x in proc.move_ids if x.picking_id and not x.picking_id.invoice_type_id])) + if pickings: + pick_obj.write(cr, uid, pickings, {'invoice_type_id': proc.sale_line_id.order_id.invoice_type_id.id}, context=context) + return res + + class sale(osv.osv): _inherit = "sale.order" _columns = { 'invoice_type_id': fields.many2one('sale_journal.invoice.type', 'Invoice Type', help="Generate invoice based on the selected option.") } - def _prepare_order_picking(self, cr, uid, order, context=None): - result = super(sale,self)._prepare_order_picking(cr, uid, order, context=context) - result.update(invoice_type_id=order.invoice_type_id and order.invoice_type_id.id or False) - return result - def onchange_partner_id(self, cr, uid, ids, part, context=None): result = super(sale, self).onchange_partner_id(cr, uid, ids, part, context=context) if part: From f469e8e09fd2b499ef8b157ba6a57465d56d978c Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Fri, 14 Nov 2014 11:13:38 +0100 Subject: [PATCH 29/52] [FIX] layout: add alt on logo for seo --- addons/website/views/website_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 714c49fa046..f4ecc8c521f 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -173,7 +173,7 @@ From 7f4c4a5e8a94c4cea0700a3fcc928fa11fbdc96f Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 13 Nov 2014 18:01:25 +0100 Subject: [PATCH 30/52] [IMP] purchase: error message linked to product, not company --- addons/purchase/purchase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index d1bb845e38d..e6f4989c9a3 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -471,7 +471,7 @@ class purchase_order(osv.osv): if not acc_id: acc_id = po_line.product_id.categ_id.property_account_expense_categ.id if not acc_id: - raise osv.except_osv(_('Error!'), _('Define expense account for this company: "%s" (id:%d).') % (po_line.product_id.name, po_line.product_id.id,)) + raise osv.except_osv(_('Error!'), _('Define expense account for this product: "%s" (id:%d).') % (po_line.product_id.name, po_line.product_id.id,)) else: acc_id = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context=context).id fpos = po_line.order_id.fiscal_position or False From cf488682c82a320589b1241f4b6bd9d59018bfed Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 14 Nov 2014 11:10:15 +0100 Subject: [PATCH 31/52] [FIX] stock: multicompany reception When a picking is confirmed, the generated account.move(.line) should take the company, accounts, journals and period with the same company as the picking, not the one of the current user. This was problematic if a user in a company confirm a picking linked to a purchase order done in another company. For real time valuations, the generated accounting entries were mixing both companies. Fixes #3466 --- addons/stock/stock.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 16565871de3..bd15c45e421 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2355,6 +2355,9 @@ class stock_move(osv.osv): context = {} src_company_ctx = dict(context,force_company=move.location_id.company_id.id) dest_company_ctx = dict(context,force_company=move.location_dest_id.company_id.id) + # do not take the company of the one of the user + # used to select the correct period + company_ctx = dict(context, company_id=move.company_id.id) account_moves = [] # Outgoing moves (or cross-company output part) if move.location_id.company_id \ @@ -2386,7 +2389,8 @@ class stock_move(osv.osv): { 'journal_id': j_id, 'line_id': move_lines, - 'ref': move.picking_id and move.picking_id.name}, context=context) + 'company_id': move.company_id.id, + 'ref': move.picking_id and move.picking_id.name}, context=company_ctx) def action_done(self, cr, uid, ids, context=None): """ Makes the move done and if all moves are done, it will finish the picking. From 5b2f13abdfed218db9098325061cd6b14eac0d3c Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 14 Nov 2014 13:40:02 +0100 Subject: [PATCH 32/52] [FIX] product: more accurate name_search First, name_search searches on default_code, then, if the limit is not reached, it searches on the product name The results found from the default code search must be removed from the search domain when doing the search on the product name, to avoid having results already found by the search on the default_code opw-618015 --- addons/product/product.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/product/product.py b/addons/product/product.py index b70fa030dd0..ce5b9bacddf 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -691,7 +691,7 @@ class product_product(osv.osv): ids.update(self.search(cr, user, args + [('default_code',operator,name)], limit=limit, context=context)) if not limit or len(ids) < limit: # we may underrun the limit because of dupes in the results, that's fine - ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit and (limit-len(ids)) or False) , context=context)) + ids.update(self.search(cr, user, args + [('name',operator,name), ('id', 'not in', list(ids))], limit=(limit and (limit-len(ids)) or False) , context=context)) ids = list(ids) elif not ids and operator in expression.NEGATIVE_TERM_OPERATORS: ids = self.search(cr, user, args + ['&', ('default_code', operator, name), ('name', operator, name)], limit=limit, context=context) From 3613b74df37a5a0c7bc2479a4b18471e6036e0a9 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 14 Nov 2014 14:14:58 +0100 Subject: [PATCH 33/52] [FIX] point_of_sale: missing multi-company rule --- addons/point_of_sale/security/point_of_sale_security.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addons/point_of_sale/security/point_of_sale_security.xml b/addons/point_of_sale/security/point_of_sale_security.xml index 47cdf47fda4..4804dc20dc8 100644 --- a/addons/point_of_sale/security/point_of_sale_security.xml +++ b/addons/point_of_sale/security/point_of_sale_security.xml @@ -25,5 +25,11 @@ ['|',('shop_id.company_id','=',False),('shop_id.company_id','child_of',[user.company_id.id])] + + Point Of Sale Order Analysis multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + From 817ce8f912070c34d36fe93abe65262a3ca53e3e Mon Sep 17 00:00:00 2001 From: ged-odoo Date: Fri, 14 Nov 2014 14:38:15 +0100 Subject: [PATCH 34/52] [IMP] add link to write method in documentation --- doc/howtos/backend.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/howtos/backend.rst b/doc/howtos/backend.rst index bb300ae51c8..bbf173cb5c4 100644 --- a/doc/howtos/backend.rst +++ b/doc/howtos/backend.rst @@ -1384,7 +1384,7 @@ access rights are limited. Here is an example of a rule that prevents the deletion of leads that are not in state ``cancel``. Notice that the value of the field ``groups`` must follow -the same convention as the method ``write`` of the ORM. +the same convention as the method :meth:`~openerp.models.Model.write` of the ORM. .. code-block:: xml From 35404841382c37d2553c153d92d29309cbade4e9 Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Fri, 14 Nov 2014 15:45:13 +0100 Subject: [PATCH 35/52] [FIX] account: allow to use quantity in taxes computated with python code --- addons/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/account.py b/addons/account/account.py index df9a52dd0d8..2c4dc13710d 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -2022,7 +2022,7 @@ class account_tax(osv.osv): data['tax_amount']=quantity # data['amount'] = quantity elif tax.type=='code': - localdict = {'price_unit':cur_price_unit, 'product':product, 'partner':partner} + localdict = {'price_unit':cur_price_unit, 'product':product, 'partner':partner, 'quantity': quantity} exec tax.python_compute in localdict amount = localdict['result'] data['amount'] = amount From da15c9d27b420e7643048dfab07aebcf94b92b4a Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 14 Nov 2014 15:49:39 +0100 Subject: [PATCH 36/52] [FIX] web: do not set the one2many dirty on field validation This rev. 06104ba553702b16878d52c5b8ab089355216a5e Added the dirty flag on the o2m field when the editor of the editable list was enabled (meaning that the editable list has been altered)) because the dirty flag was not set correctly by the one2many during the edition, at the time. It looks like this is now the case Besides, as now, we valid all the editable list of the form, wether or not the editable list was altered, we must not set the o2m as dirty anymore. --- addons/web/static/src/js/view_form.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 819920f89b3..be16faa309a 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3885,7 +3885,6 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ if (!this.fields_view || !this.editable()){ return true; } - this.o2m._dirty_flag = true; var r; return _.every(this.records.records, function(record){ r = record; From eb2e53276ace523f5cb852b8427f5ea9a2f97fc4 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Fri, 14 Nov 2014 16:19:33 +0100 Subject: [PATCH 37/52] [FIX] layout - use company name for attribut alt of company logo --- addons/website/views/website_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index f4ecc8c521f..917fd0a2f7e 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -173,7 +173,7 @@ From 0a7633c11ea0956894088d6b742fc9ce251fb896 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 14 Nov 2014 16:51:13 +0100 Subject: [PATCH 38/52] [FIX] purchase: keep PO currency on picking The delivery of a purchase order was not keeping the currency and cost price from the purchase order for the reception. This was problematic for orders where the invoice was generated from the picking (Invoicing Control: Based on incoming shipments). The currency of the purchase order was kept while the cost was the one in the company's currency. It's better to keep the currency of the purchase order to make the invoice as it's usually the one expected (and not convert everything to the currency of the company). opw 615555 --- addons/purchase/stock.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index e8ea8f8f067..c211f39e719 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -147,6 +147,16 @@ class stock_partial_picking(osv.osv_memory): return {'cost': cost, 'currency': company_currency} return super(stock_partial_picking, self)._product_cost_for_average_update(cr, uid, move) + def _partial_move_for(self, cr, uid, move, context=None): + partial_move = super(stock_partial_picking, self)._partial_move_for(cr, uid, move, context=context) + if move.picking_id.purchase_id and move.purchase_line_id: + pur_currency = move.purchase_line_id.order_id.currency_id.id + partial_move.update({ + 'currency': pur_currency, + 'cost': move.purchase_line_id.price_unit + }) + return partial_move + def __get_help_text(self, cursor, user, picking_id, context=None): picking = self.pool.get('stock.picking').browse(cursor, user, picking_id, context=context) if picking.purchase_id: From 922a52dcde24749aa3e640a940be462b85c075dd Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 14 Nov 2014 17:57:10 +0100 Subject: [PATCH 39/52] [FIX] web: date autocompletion should not harcode 'date' But should use date or datetime according to the widget type This fix is related to 69d60465ee67969d72210a61a9e23204d037378a --- addons/web/static/src/js/search.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 89cf5521db1..d2db5b55213 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1497,10 +1497,18 @@ instance.web.search.DateField = instance.web.search.Field.extend(/** @lends inst return instance.web.date_to_str(facetValue.get('value')); }, complete: function (needle) { + var d; try { - var d = instance.web.str_to_date(instance.web.parse_value(needle, {'widget': 'date'})); + var t = (this.attrs && this.attrs.type === 'datetime') ? 'datetime' : 'date'; + var v = instance.web.parse_value(needle, {'widget': t}); + if (t === 'datetime'){ + d = instance.web.str_to_datetime(v); + } + else{ + d = instance.web.str_to_date(v); + } } catch (e) { - return false; + // pass } if (!d) { return $.when(null); } var date_string = instance.web.format_value(d, this.attrs); From b213438176b16e96686d94063a3cdf6dc5c49618 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 13 Nov 2014 17:48:21 +0100 Subject: [PATCH 40/52] [IMP] Remove unnecessary refresh statements because of new api --- addons/mrp/procurement.py | 1 - addons/procurement/procurement.py | 1 - addons/purchase/test/process/run_scheduler.yml | 1 - addons/sale/sale.py | 1 - addons/stock/stock.py | 12 ------------ addons/stock_account/stock_account.py | 1 - 6 files changed, 17 deletions(-) diff --git a/addons/mrp/procurement.py b/addons/mrp/procurement.py index 0c17cd1cc76..58727d9c160 100644 --- a/addons/mrp/procurement.py +++ b/addons/mrp/procurement.py @@ -111,7 +111,6 @@ class procurement_order(osv.osv): res[procurement.id] = produce_id self.write(cr, uid, [procurement.id], {'production_id': produce_id}) - procurement.refresh() self.production_order_create_note(cr, uid, procurement, context=context) production_obj.action_compute(cr, uid, [produce_id], properties=[x.id for x in procurement.property_ids]) production_obj.signal_workflow(cr, uid, [produce_id], 'button_confirm') diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index 12d40a39be1..085d620cc87 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -203,7 +203,6 @@ class procurement_order(osv.osv): if procurement.state not in ("running", "done"): try: if self._assign(cr, uid, procurement, context=context): - procurement.refresh() res = self._run(cr, uid, procurement, context=context or {}) if res: self.write(cr, uid, [procurement.id], {'state': 'running'}, context=context) diff --git a/addons/purchase/test/process/run_scheduler.yml b/addons/purchase/test/process/run_scheduler.yml index 01aa497311d..3ce573584a4 100644 --- a/addons/purchase/test/process/run_scheduler.yml +++ b/addons/purchase/test/process/run_scheduler.yml @@ -38,5 +38,4 @@ !python {model: procurement.order}: | procurement = self.browse(cr, uid, ref('procurement_order_testcase0')) procurement.purchase_line_id.unlink() - procurement.refresh() assert procurement.state == 'cancel', 'Procurement should be cancelled' diff --git a/addons/sale/sale.py b/addons/sale/sale.py index d1736a22cf0..b5593d60dda 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -709,7 +709,6 @@ class sale_order(osv.osv): if line.procurement_ids: #first check them to see if they are in exception or not (one of the related moves is cancelled) procurement_obj.check(cr, uid, [x.id for x in line.procurement_ids if x.state not in ['cancel', 'done']]) - line.refresh() #run again procurement that are in exception in order to trigger another move proc_ids += [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')] procurement_obj.reset_to_confirmed(cr, uid, proc_ids, context=context) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 902a9f287e9..76732d53ed6 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -402,7 +402,6 @@ class stock_quant(osv.osv): quant = self._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=location_from, force_location_to=location_to, context=context) else: self._quant_split(cr, uid, quant, qty, context=context) - quant.refresh() to_move_quants.append(quant) quants_reconcile.append(quant) if to_move_quants: @@ -412,7 +411,6 @@ class stock_quant(osv.osv): if location_to.usage == 'internal': if self.search(cr, uid, [('product_id', '=', move.product_id.id), ('qty','<', 0)], limit=1, context=context): for quant in quants_reconcile: - quant.refresh() self._quant_reconcile_negative(cr, uid, quant, move, context=context) def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, context=None): @@ -520,7 +518,6 @@ class stock_quant(osv.osv): return False new_quant = self.copy(cr, SUPERUSER_ID, quant.id, default={'qty': quant.qty - qty}, context=context) self.write(cr, SUPERUSER_ID, quant.id, {'qty': qty}, context=context) - quant.refresh() return self.browse(cr, uid, new_quant, context=context) def _get_latest_move(self, cr, uid, quant, context=None): @@ -864,7 +861,6 @@ class stock_picking(osv.osv): for pick in self.browse(cr, uid, ids, context=context): if pick.state == 'draft': self.action_confirm(cr, uid, [pick.id], context=context) - pick.refresh() #skip the moves that don't need to be checked move_ids = [x.id for x in pick.move_lines if x.state not in ('draft', 'cancel', 'done')] if not move_ids: @@ -1341,9 +1337,7 @@ class stock_picking(osv.osv): if not all_op_processed: todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context) - picking.refresh() #split move lines eventually - toassign_move_ids = [] for move in picking.move_lines: remaining_qty = move.remaining_qty @@ -1368,7 +1362,6 @@ class stock_picking(osv.osv): self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context) elif context.get('do_only_split'): context = dict(context, split=todo_move_ids) - picking.refresh() self._create_backorder(cr, uid, picking, context=context) if toassign_move_ids: stock_move_obj.action_assign(cr, uid, toassign_move_ids, context=context) @@ -2170,7 +2163,6 @@ class stock_move(osv.osv): for move in todo_moves: if move.linked_move_operation_ids: continue - move.refresh() #then if the move isn't totally assigned, try to find quants without any specific domain if move.state != 'assigned': qty_already_assigned = move.reserved_availability @@ -2571,7 +2563,6 @@ class stock_inventory(osv.osv): if inventory_line.product_qty < 0 and inventory_line.product_qty != inventory_line.theoretical_qty: raise osv.except_osv(_('Warning'), _('You cannot set a negative product quantity in an inventory line:\n\t%s - qty: %s' % (inventory_line.product_id.name, inventory_line.product_qty))) self.action_check(cr, uid, [inv.id], context=context) - inv.refresh() self.write(cr, uid, [inv.id], {'state': 'done'}, context=context) self.post_inventory(cr, uid, inv, context=context) return True @@ -3317,7 +3308,6 @@ class stock_warehouse(osv.osv): new_id = super(stock_warehouse, self).create(cr, uid, vals=vals, context=context) warehouse = self.browse(cr, uid, new_id, context=context) self.create_sequences_and_picking_types(cr, uid, warehouse, context=context) - warehouse.refresh() #create routes and push/pull rules new_objects_dict = self.create_routes(cr, uid, new_id, warehouse, context=context) @@ -3437,7 +3427,6 @@ class stock_warehouse(osv.osv): self.change_route(cr, uid, ids, warehouse, vals.get('reception_steps', False), vals.get('delivery_steps', False), context=context_with_inactive) # Check if we need to change something to resupply warehouses and associated MTO rules self._check_resupply(cr, uid, warehouse, vals.get('reception_steps'), vals.get('delivery_steps'), context=context) - warehouse.refresh() if vals.get('code') or vals.get('name'): name = warehouse.name #rename sequence @@ -3579,7 +3568,6 @@ class stock_location_path(osv.osv): 'date_expected': newdate, 'location_dest_id': rule.location_dest_id.id }) - move.refresh() #avoid looping if a push rule is not well configured if rule.location_dest_id.id != old_dest_location: #call again push_apply to see if a next step is defined diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index 9707263391a..243d982f8f6 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -267,7 +267,6 @@ class stock_move(osv.osv): def _store_average_cost_price(self, cr, uid, move, context=None): ''' move is a browe record ''' product_obj = self.pool.get('product.product') - move.refresh() if any([q.qty <= 0 for q in move.quant_ids]): #if there is a negative quant, the standard price shouldn't be updated return From 2f5ca4380ad635fcbe33983ff63c2f88a8d8470b Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Mon, 17 Nov 2014 09:44:10 +0100 Subject: [PATCH 41/52] Revert "[IMP] Remove unnecessary refresh statements because of new api" This reverts commit b213438176b16e96686d94063a3cdf6dc5c49618. --- addons/mrp/procurement.py | 1 + addons/procurement/procurement.py | 1 + addons/purchase/test/process/run_scheduler.yml | 1 + addons/sale/sale.py | 1 + addons/stock/stock.py | 12 ++++++++++++ addons/stock_account/stock_account.py | 1 + 6 files changed, 17 insertions(+) diff --git a/addons/mrp/procurement.py b/addons/mrp/procurement.py index 58727d9c160..0c17cd1cc76 100644 --- a/addons/mrp/procurement.py +++ b/addons/mrp/procurement.py @@ -111,6 +111,7 @@ class procurement_order(osv.osv): res[procurement.id] = produce_id self.write(cr, uid, [procurement.id], {'production_id': produce_id}) + procurement.refresh() self.production_order_create_note(cr, uid, procurement, context=context) production_obj.action_compute(cr, uid, [produce_id], properties=[x.id for x in procurement.property_ids]) production_obj.signal_workflow(cr, uid, [produce_id], 'button_confirm') diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index 085d620cc87..12d40a39be1 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -203,6 +203,7 @@ class procurement_order(osv.osv): if procurement.state not in ("running", "done"): try: if self._assign(cr, uid, procurement, context=context): + procurement.refresh() res = self._run(cr, uid, procurement, context=context or {}) if res: self.write(cr, uid, [procurement.id], {'state': 'running'}, context=context) diff --git a/addons/purchase/test/process/run_scheduler.yml b/addons/purchase/test/process/run_scheduler.yml index 3ce573584a4..01aa497311d 100644 --- a/addons/purchase/test/process/run_scheduler.yml +++ b/addons/purchase/test/process/run_scheduler.yml @@ -38,4 +38,5 @@ !python {model: procurement.order}: | procurement = self.browse(cr, uid, ref('procurement_order_testcase0')) procurement.purchase_line_id.unlink() + procurement.refresh() assert procurement.state == 'cancel', 'Procurement should be cancelled' diff --git a/addons/sale/sale.py b/addons/sale/sale.py index b5593d60dda..d1736a22cf0 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -709,6 +709,7 @@ class sale_order(osv.osv): if line.procurement_ids: #first check them to see if they are in exception or not (one of the related moves is cancelled) procurement_obj.check(cr, uid, [x.id for x in line.procurement_ids if x.state not in ['cancel', 'done']]) + line.refresh() #run again procurement that are in exception in order to trigger another move proc_ids += [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')] procurement_obj.reset_to_confirmed(cr, uid, proc_ids, context=context) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 76732d53ed6..902a9f287e9 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -402,6 +402,7 @@ class stock_quant(osv.osv): quant = self._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=location_from, force_location_to=location_to, context=context) else: self._quant_split(cr, uid, quant, qty, context=context) + quant.refresh() to_move_quants.append(quant) quants_reconcile.append(quant) if to_move_quants: @@ -411,6 +412,7 @@ class stock_quant(osv.osv): if location_to.usage == 'internal': if self.search(cr, uid, [('product_id', '=', move.product_id.id), ('qty','<', 0)], limit=1, context=context): for quant in quants_reconcile: + quant.refresh() self._quant_reconcile_negative(cr, uid, quant, move, context=context) def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, context=None): @@ -518,6 +520,7 @@ class stock_quant(osv.osv): return False new_quant = self.copy(cr, SUPERUSER_ID, quant.id, default={'qty': quant.qty - qty}, context=context) self.write(cr, SUPERUSER_ID, quant.id, {'qty': qty}, context=context) + quant.refresh() return self.browse(cr, uid, new_quant, context=context) def _get_latest_move(self, cr, uid, quant, context=None): @@ -861,6 +864,7 @@ class stock_picking(osv.osv): for pick in self.browse(cr, uid, ids, context=context): if pick.state == 'draft': self.action_confirm(cr, uid, [pick.id], context=context) + pick.refresh() #skip the moves that don't need to be checked move_ids = [x.id for x in pick.move_lines if x.state not in ('draft', 'cancel', 'done')] if not move_ids: @@ -1337,7 +1341,9 @@ class stock_picking(osv.osv): if not all_op_processed: todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context) + picking.refresh() #split move lines eventually + toassign_move_ids = [] for move in picking.move_lines: remaining_qty = move.remaining_qty @@ -1362,6 +1368,7 @@ class stock_picking(osv.osv): self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context) elif context.get('do_only_split'): context = dict(context, split=todo_move_ids) + picking.refresh() self._create_backorder(cr, uid, picking, context=context) if toassign_move_ids: stock_move_obj.action_assign(cr, uid, toassign_move_ids, context=context) @@ -2163,6 +2170,7 @@ class stock_move(osv.osv): for move in todo_moves: if move.linked_move_operation_ids: continue + move.refresh() #then if the move isn't totally assigned, try to find quants without any specific domain if move.state != 'assigned': qty_already_assigned = move.reserved_availability @@ -2563,6 +2571,7 @@ class stock_inventory(osv.osv): if inventory_line.product_qty < 0 and inventory_line.product_qty != inventory_line.theoretical_qty: raise osv.except_osv(_('Warning'), _('You cannot set a negative product quantity in an inventory line:\n\t%s - qty: %s' % (inventory_line.product_id.name, inventory_line.product_qty))) self.action_check(cr, uid, [inv.id], context=context) + inv.refresh() self.write(cr, uid, [inv.id], {'state': 'done'}, context=context) self.post_inventory(cr, uid, inv, context=context) return True @@ -3308,6 +3317,7 @@ class stock_warehouse(osv.osv): new_id = super(stock_warehouse, self).create(cr, uid, vals=vals, context=context) warehouse = self.browse(cr, uid, new_id, context=context) self.create_sequences_and_picking_types(cr, uid, warehouse, context=context) + warehouse.refresh() #create routes and push/pull rules new_objects_dict = self.create_routes(cr, uid, new_id, warehouse, context=context) @@ -3427,6 +3437,7 @@ class stock_warehouse(osv.osv): self.change_route(cr, uid, ids, warehouse, vals.get('reception_steps', False), vals.get('delivery_steps', False), context=context_with_inactive) # Check if we need to change something to resupply warehouses and associated MTO rules self._check_resupply(cr, uid, warehouse, vals.get('reception_steps'), vals.get('delivery_steps'), context=context) + warehouse.refresh() if vals.get('code') or vals.get('name'): name = warehouse.name #rename sequence @@ -3568,6 +3579,7 @@ class stock_location_path(osv.osv): 'date_expected': newdate, 'location_dest_id': rule.location_dest_id.id }) + move.refresh() #avoid looping if a push rule is not well configured if rule.location_dest_id.id != old_dest_location: #call again push_apply to see if a next step is defined diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index 243d982f8f6..9707263391a 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -267,6 +267,7 @@ class stock_move(osv.osv): def _store_average_cost_price(self, cr, uid, move, context=None): ''' move is a browe record ''' product_obj = self.pool.get('product.product') + move.refresh() if any([q.qty <= 0 for q in move.quant_ids]): #if there is a negative quant, the standard price shouldn't be updated return From 956bb08433b9a963686df6553b5ca940600d2ada Mon Sep 17 00:00:00 2001 From: hiekmann Date: Mon, 17 Nov 2014 09:22:23 +0000 Subject: [PATCH 42/52] [fix] portal: typo in description closes #3685 --- addons/portal/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/portal/__openerp__.py b/addons/portal/__openerp__.py index 361971b0d92..042f2f20381 100644 --- a/addons/portal/__openerp__.py +++ b/addons/portal/__openerp__.py @@ -33,7 +33,7 @@ Customize access to your OpenERP database to external users by creating portals. ================================================================================ A portal defines a specific user menu and access rights for its members. This -menu can ben seen by portal members, public users and any other user that +menu can be seen by portal members, public users and any other user that have the access to technical features (e.g. the administrator). Also, each portal member is linked to a specific partner. From 2052c16d21d50596adc11060a8a7eee21eccb8d9 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 17 Nov 2014 11:35:06 +0100 Subject: [PATCH 43/52] [FIX] ir_model: on a custom model, _rec_name should be 'x_name' if it exists --- openerp/addons/base/ir/ir_model.py | 7 +------ openerp/models.py | 2 ++ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index 307b32516a3..c50044d3ff9 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -228,12 +228,7 @@ class ir_model(osv.osv): _module = False _custom = True - obj = CustomModel._build_model(self.pool, cr) - obj._rec_name = CustomModel._rec_name = ( - 'x_name' if 'x_name' in obj._columns else - list(obj._columns)[0] if obj._columns else - 'id' - ) + CustomModel._build_model(self.pool, cr) class ir_model_fields(osv.osv): _name = 'ir.model.fields' diff --git a/openerp/models.py b/openerp/models.py index df46fce4d5d..10ccb8955b9 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -2991,6 +2991,8 @@ class BaseModel(object): "Invalid rec_name %s for model %s" % (cls._rec_name, cls._name) elif 'name' in cls._fields: cls._rec_name = 'name' + elif 'x_name' in cls._fields: + cls._rec_name = 'x_name' def fields_get(self, cr, user, allfields=None, context=None, write_access=True): """ fields_get([fields]) From f302c123ea8d0b989fe6aa6517e26783f69d62b1 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 17 Nov 2014 14:07:36 +0100 Subject: [PATCH 44/52] [IMP] doc: indexify index * opt-in toc navification * unhide index toctree, it's now back to a list of documents * remove text thing --- doc/_themes/odoodoc/layout.html | 5 +-- doc/_themes/odoodoc/sphinx_monkeypatch.py | 39 +++++++++------------- doc/images/view-on-github.png | Bin 1493 -> 0 bytes doc/index.rst | 28 ++-------------- doc/reference/cmdline.rst | 2 +- 5 files changed, 22 insertions(+), 52 deletions(-) delete mode 100644 doc/images/view-on-github.png diff --git a/doc/_themes/odoodoc/layout.html b/doc/_themes/odoodoc/layout.html index 7685d8af25f..1a68d31814a 100644 --- a/doc/_themes/odoodoc/layout.html +++ b/doc/_themes/odoodoc/layout.html @@ -32,7 +32,7 @@
{{ toctree(maxdepth=4, collapse=False, includehidden=True, - main_navbar=False, titles_only=False) }} + navbar='side', titles_only=False) }} {% if github_link %}

Edit on GitHub @@ -72,7 +72,8 @@ {% endif %}

diff --git a/doc/_themes/odoodoc/sphinx_monkeypatch.py b/doc/_themes/odoodoc/sphinx_monkeypatch.py index 13c3a305988..94e063e1f44 100644 --- a/doc/_themes/odoodoc/sphinx_monkeypatch.py +++ b/doc/_themes/odoodoc/sphinx_monkeypatch.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import sphinx.roles import sphinx.environment -from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.writers.html import HTMLTranslator from docutils.writers.html4css1 import HTMLTranslator as DocutilsTranslator @@ -9,27 +8,17 @@ def patch(): # navify toctree (oh god) @monkey(sphinx.environment.BuildEnvironment) def resolve_toctree(old_resolve, self, *args, **kwargs): - """ If main_navbar, bootstrapify TOC to yield a navbar + """ If navbar, bootstrapify TOC to yield a navbar """ - main_navbar = kwargs.pop('main_navbar', False) + navbar = kwargs.pop('navbar', None) toc = old_resolve(self, *args, **kwargs) if toc is None: return None - navbarify(toc[0], main_navbar=main_navbar) + navbarify(toc[0], navbar=navbar) return toc - @monkey(StandaloneHTMLBuilder) - def _get_local_toctree(old_local, self, *args, **kwargs): - """ _get_local_toctree generates a documentation toctree for the local - document (?), called from handle_page - """ - # so can call toctree(main_navbar=False) - d = {'main_navbar': True} - d.update(kwargs) - return old_local(self, *args, **d) - # monkeypatch visit_table to remove border and add .table HTMLTranslator.visit_table = visit_table # disable colspec crap @@ -37,11 +26,18 @@ def patch(): # copy data- attributes straight from source to dest HTMLTranslator.starttag = starttag_data -def navbarify(node, main_navbar=False): - # add classes to toplevel - if not main_navbar: - navify([node]) - else: +def navbarify(node, navbar=None): + """ + :param node: toctree node to navbarify + :param navbar: Whether this toctree is a 'main' navbar, a 'side' navbar or + not a navbar at all + """ + if navbar == 'side': + for n in node.traverse(): + if n.tagname == 'bullet_list': + n['classes'].append('nav') + elif navbar == 'main': + # add classes to just toplevel node['classes'].extend(['nav', 'navbar-nav', 'navbar-right']) for list_item in node.children: # bullet_list @@ -59,11 +55,6 @@ def navbarify(node, main_navbar=False): link.attributes['data-toggle'] = 'dropdown' # list_item.bullet_list list_item.children[1]['classes'].append('dropdown-menu') -def navify(nodes): - for node in nodes: - if node.tagname == 'bullet_list': - node['classes'].append('nav') - navify(node.children) def visit_table(self, node): """ diff --git a/doc/images/view-on-github.png b/doc/images/view-on-github.png deleted file mode 100644 index 65a8d42363de1b3414c175ca606491f74ca30b8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1493 zcmV;`1uFW9P)E#qEfixqm!?j-ouZ!$83qFc*e1Co|>7NnwzQk`TD7yny9|uov6m$z^3-( z#K__Iy1cyW)VbjF{p;)M|Nr#t`~Shv^3>1S!>5`3_uS*z-_*CuexokL000FrNklGt*M7|qa1qmX; zfv`lt&pTRl2+O>qI9X3j0%nsOgvY`PGZ!aenPd2I1(3Bm@PfN2>Mt-M%ud=4WsGz( z*d1^{?reEV4mkk@(h|MqV|W#UYz0yr^3B-TXc5KvT?&Ged?@M4w6cK;T-peq;LU-M zy1RLYw=(Aj^w{BVwNE-y@l2MZ_fM%#mR;LtWzp$O zMc8(%Y`dK;3LhcMGb;sQ;t%;wa8%IEk}GeH9gy4;-dxzciTY7O0i>D3G4yN#LBZ|w zMw%yUYW{pvxyH{_LZ0op`dzO$WE*dwH`CD2(9qD((9qD(&{0ZR&T!HE^IFdzDU)_5 z%+}I$UCVmQDRqKQ=qe!Xf6QEeo_0l0(l|T0jLANyOZQ;&oFaU(Ij*m#_I*W|DiB_5 z^K^B|-inRWA7!f!@Yc++PggktNzRjvDd{9T!lFhu&YZcZZq;i-Gg{}FMd%ZkFS8*l zVXM$DYly}*eMtyptB`Vzm$9mu$TS-WHumWGCxPcG*TQt0}8?`<*lK( z|3OwQ15J$TpMG}UA~}TQc+Kr*jttkR8G9WMrP}cHJ_I8KysI-028eR2RKMn2mKaVlRDXUj47Y|N)dN} zkye@5clbEAS}e`=rhIAZRX4{rkg&})mH=&l;)#Ydniq^_g@w1|9G8=M!Pu2X*pbhfEY vZE91S+VojRf1BDg8jVJy(P%UpjYjhemMMbnS8-)|00000NkvXXu0mjfAWiiM diff --git a/doc/index.rst b/doc/index.rst index 3c1696020d6..0cd25cdd904 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -4,34 +4,12 @@ odoo developer documentation Welcome to the Odoo developer documentation. -This documentation is incomplete and may contain errors, if you wish to -contribute, every page should have a :guilabel:`View on Github` link: - -.. image:: images/view-on-github.* - :align: center - -Through this link you can edit documents and submit changes for review using -`github's web interface -`_. -Contributions are welcome and appreciated. - .. todo:: what's the documentation's license? -The documentation is currently organized in four sections: - -* :doc:`tutorials`, aimed at introducing the primary areas of developing Odoo - modules -* :doc:`reference`, which ought be the complete and canonical documentation - for Odoo subsystems -* :doc:`modules`, documenting useful specialized modules and integration - methods (and currently empty) - -.. hidden toctree w/o titlesonly otherwise the titlesonly "sticks" to - in-document toctrees and we can't have a toctree showing both "sibling" - pages and current document sections - +.. titlesonly breaks level 3 (~in-document) toc of left navbar, so use + maxdepth instead .. toctree:: - :hidden: + :maxdepth: 2 tutorials reference diff --git a/doc/reference/cmdline.rst b/doc/reference/cmdline.rst index 32c1ef48729..1ec7d96ae9d 100644 --- a/doc/reference/cmdline.rst +++ b/doc/reference/cmdline.rst @@ -71,7 +71,7 @@ Scaffolding is available via the :command:`odoo.py scaffold` subcommand. .. option:: -t