From 20ed57f874176161a043dee834b25c7f0b3dc175 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Tue, 6 Jun 2017 16:48:38 +0200 Subject: [PATCH 01/24] [FIX] calendar: _get_recurrent_date_by_event Let's create in google calendar a reccurent event with all day set and a end date. When synchronising google calendar with Odoo(by clicking on button "Sync with Google"). It raised on error because the start date had a time zone but not the end date(the UNTIL in the rrule). opw:745665 --- addons/calendar/models/calendar.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index 8db6909b847..8c18fe2e24b 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -553,15 +553,18 @@ class Meeting(models.Model): if not event_date: event_date = datetime.now() - # Convert the event date to saved timezone (or context tz) as it'll - # define the correct hour/day asked by the user to repeat for recurrence. - event_date = event_date.astimezone(timezone) # transform "+hh:mm" timezone - rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date, forceset=True) + if self.allday and self.rrule and 'Z' not in self.rrule: + rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date.replace(tzinfo=None), forceset=True, ignoretz=True) + else: + # Convert the event date to saved timezone (or context tz) as it'll + # define the correct hour/day asked by the user to repeat for recurrence. + event_date = event_date.astimezone(timezone) # transform "+hh:mm" timezone + rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date, forceset=True, tzinfos={}) recurring_meetings = self.search([('recurrent_id', '=', self.id), '|', ('active', '=', False), ('active', '=', True)]) for meeting in recurring_meetings: rset1._exdate.append(todate(meeting.recurrent_id_date)) - return [d.astimezone(pytz.UTC) for d in rset1] + return [d.astimezone(pytz.UTC) if d.tzinfo else d for d in rset1] @api.multi def _get_recurrency_end_date(self): From c638fbdf232b39585749405fe7d7d5a225041ad9 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 7 Jun 2017 11:18:56 +0200 Subject: [PATCH 02/24] [FIX] stock: ACL for product attributes for Inventory managers If only Inventory is installed, only the admin could create new product attributes for products variants. Other managers couldn't. This revision adds the ACL according to what is done in other modules, such as in `sale`. Mostlikely, we should create a product manager group, for which this rights are given, and make the other groups (sales manager, inventory manager) inherit from this one. opw-745735 --- addons/stock/security/ir.model.access.csv | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv index 3eef34a4bda..0f540b9aeb1 100644 --- a/addons/stock/security/ir.model.access.csv +++ b/addons/stock/security/ir.model.access.csv @@ -75,3 +75,7 @@ access_barcode_rule_stock_manager,barcode.rule.stock.manager,barcodes.model_barc access_stock_forecast_user,report.stock.forecast.user,model_report_stock_forecast,stock.group_stock_user,1,0,0,0 access_stock_forecast_manager,report.stock.forecast.manager,model_report_stock_forecast,stock.group_stock_manager,1,1,1,1 access_stock_scrap_user,stock.scrap.user,model_stock_scrap,stock.group_stock_user,1,1,1,0 +access_product_attribute_manager,product.attribute manager,product.model_product_attribute,stock.group_stock_manager,1,1,1,1 +access_product_attribute_value_manager,product.attribute manager value,product.model_product_attribute_value,stock.group_stock_manager,1,1,1,1 +access_product_attribute_price_manager,product.attribute manager price,product.model_product_attribute_price,stock.group_stock_manager,1,1,1,1 +access_product_attribute_line_manager,product.attribute manager line,product.model_product_attribute_line,stock.group_stock_manager,1,1,1,1 From 86a0d31251a701da8bd1dbcb5580b958d932b33a Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Wed, 7 Jun 2017 13:29:58 +0200 Subject: [PATCH 03/24] [FIX] account: Validating a statement with old posted moves When validating a statement with old posted moves, the lock date was checked by function, _check_lock_date and then it was impossible to validate the statement even if all the moves were already posted. --- addons/account/models/account_bank_statement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py index 857ffc3e71d..1728aecbe33 100644 --- a/addons/account/models/account_bank_statement.py +++ b/addons/account/models/account_bank_statement.py @@ -241,7 +241,7 @@ class AccountBankStatement(models.Model): for aml in st_line.journal_entry_ids: moves |= aml.move_id if moves: - moves.post() + moves.filtered(lambda m: m.state != 'posted').post() statement.message_post(body=_('Statement %s confirmed, journal items were created.') % (statement.name,)) statements.link_bank_to_partner() statements.write({'state': 'confirm', 'date_done': time.strftime("%Y-%m-%d %H:%M:%S")}) From 7b210167604d4450d0b52dfddd39ca3ccc672564 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 6 Jun 2017 20:25:59 +0200 Subject: [PATCH 04/24] [FIX] stock: unlink scrap Forbids to unlink a done scrap. opw-746294 --- addons/stock/i18n/stock.pot | 8 +++++++- addons/stock/models/stock_scrap.py | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/addons/stock/i18n/stock.pot b/addons/stock/i18n/stock.pot index e7182b88541..e01b6291165 100644 --- a/addons/stock/i18n/stock.pot +++ b/addons/stock/i18n/stock.pot @@ -6566,7 +6566,13 @@ msgid "You cannot cancel a stock move that has been set to 'Done'." msgstr "" #. module: stock -#: code:addons/stock/models/stock_inventory.py:352 +#: code:addons/stock/models/stock_scrap.py:72 +#, python-format +msgid "You cannot delete a scrap which is done." +msgstr "" + +#. module: stock +#: code:addons/stock/models/stock_inventory.py:374 #, python-format msgid "You cannot have two inventory adjustements in state 'in Progess' with the same product(%s), same location(%s), same package, same owner and same lot. Please first validatethe first inventory adjustement with this product before creating another one." msgstr "" diff --git a/addons/stock/models/stock_scrap.py b/addons/stock/models/stock_scrap.py index 355f0139d04..16be4d9b73b 100644 --- a/addons/stock/models/stock_scrap.py +++ b/addons/stock/models/stock_scrap.py @@ -66,6 +66,12 @@ class StockScrap(models.Model): scrap.do_scrap() return scrap + @api.multi + def unlink(self): + if 'done' in self.mapped('state'): + raise UserError(_('You cannot delete a scrap which is done.')) + return super(StockScrap, self).unlink() + def _get_origin_moves(self): return self.picking_id and self.picking_id.move_lines.filtered(lambda x: x.product_id == self.product_id) From 2332df6bc7380e742e7d3dfb945124e21078087d Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Thu, 18 May 2017 09:55:41 +0200 Subject: [PATCH 05/24] [FIX] mrp: Post inventory button without finished product Let's consider P1, P2 and P3 with a cost=1$, three stockable products where P1 and P2 are in the same product category PC1 with a Stock valuation account = SVA1 and P3 is in the product category PC2 with a Stock valuation accout = SVA2. PC1 and PC2 are set with costing method in real price and Inventory valuation = perpetual(automated). Steps to reproduce this issue: -Create a BOM where P3 is composed by 1 P1 and 1 P2 -Create a PO for P1 and P2 with a cost of 5$ for each one -Create a MO to produce 1 P3 -Check availability -Insert manually the consumed product(1 P1 and 1 P2) -The button "Post Inventory" appeared, clicked on it -Check the general ledger, two moves with debit=5$ has been created for P1 and P2 in PC1(it's ok) -Produce the MO and click on "Post Inventory" again -Check the general ledger, two moves have been created, one with a credit of 2$ on PC1 and one of debit of 2$ on PC2(it was wrong) The expected moves for the last step is: a credit of 10$ on PC1 and a debit of 10$ on PC2 because the purchase price must be considered as P1 and P2 are in real price. NB: Everything is ok if the button "Post Inventory" is clicked after the production. opw:741748 --- addons/mrp/models/mrp_production.py | 7 +++++-- addons/stock/models/product.py | 3 +++ addons/stock_account/models/product.py | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 8b0b7c48dea..097876b0b6b 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -208,8 +208,11 @@ class MrpProduction(models.Model): @api.depends('move_raw_ids.quantity_done', 'move_finished_ids.quantity_done') def _compute_post_visible(self): for order in self: - order.post_visible = any(order.move_raw_ids.filtered(lambda x: (x.quantity_done) > 0 and (x.state not in ['done', 'cancel']))) or \ - any(order.move_finished_ids.filtered(lambda x: (x.quantity_done) > 0 and (x.state not in ['done', 'cancel']))) + if order.product_tmpl_id._is_cost_method_standard(): + order.post_visible = any((x.quantity_done > 0 and x.state not in ['done', 'cancel']) for x in order.move_raw_ids) or \ + any((x.quantity_done > 0 and x.state not in ['done' 'cancel']) for x in order.move_finished_ids) + else: + order.post_visible = any((x.quantity_done > 0 and x.state not in ['done' 'cancel']) for x in order.move_finished_ids) @api.multi @api.depends('workorder_ids.state', 'move_finished_ids') diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py index b48d4f8570d..5a71c1fccf1 100644 --- a/addons/stock/models/product.py +++ b/addons/stock/models/product.py @@ -416,6 +416,9 @@ class ProductTemplate(models.Model): relation="stock.location.route", string="Category Routes", related='categ_id.total_route_ids') + def _is_cost_method_standard(self): + return True + def _compute_quantities(self): res = self._compute_quantities_dict() for template in self: diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index 1e4ac7ffbe1..1b15d2aa066 100644 --- a/addons/stock_account/models/product.py +++ b/addons/stock_account/models/product.py @@ -51,6 +51,9 @@ class ProductTemplate(models.Model): def _compute_cost_method(self): self.cost_method = self.property_cost_method or self.categ_id.property_cost_method + def _is_cost_method_standard(self): + return self.property_cost_method == 'standard' + @api.one def _set_cost_method(self): return self.write({'property_cost_method': self.cost_method}) From c0f81be3359eb87bbaf0fceee9282c0f68f740c5 Mon Sep 17 00:00:00 2001 From: Denis Vermylen Date: Wed, 7 Jun 2017 11:36:35 +0200 Subject: [PATCH 06/24] [FIX] hr_holidays: add short name_get possibility it will be used for the gantt view in enterprise --- addons/hr_holidays/models/hr_holidays.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/hr_holidays/models/hr_holidays.py b/addons/hr_holidays/models/hr_holidays.py index 9fba509ecbf..fe979d21a43 100644 --- a/addons/hr_holidays/models/hr_holidays.py +++ b/addons/hr_holidays/models/hr_holidays.py @@ -331,9 +331,12 @@ class Holidays(models.Model): res = [] for leave in self: if leave.type == 'remove': - res.append((leave.id, _("%s on %s : %.2f day(s)") % (leave.employee_id.name or leave.category_id.name, leave.holiday_status_id.name, leave.number_of_days_temp))) + if self.env.context.get('short_name'): + res.append((leave.id, _("%s : %.2f day(s)") % (leave.name or leave.holiday_status_id.name, leave.number_of_days_temp))) + else: + res.append((leave.id, _("%s on %s : %.2f day(s)") % (leave.employee_id.name or leave.category_id.name, leave.holiday_status_id.name, leave.number_of_days_temp))) else: - res.append((leave.id, _("Allocation of %s : %.2f day(s) To %s") % (leave.holiday_status_id.name, leave.number_of_days_temp,leave.employee_id.name))) + res.append((leave.id, _("Allocation of %s : %.2f day(s) To %s") % (leave.holiday_status_id.name, leave.number_of_days_temp, leave.employee_id.name))) return res def _check_state_access_right(self, vals): From 3741603495532e218b66f3cfa0272e4536f1cf2c Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Wed, 7 Jun 2017 14:11:23 +0200 Subject: [PATCH 07/24] [FIX] web: restore broken datepicker since lib update There were two main problems with the datepicker since the lib was updated: - it appeared broken in the search view "Filters" menu - it did not overflow the parent which scrolls Indeed, before the update, the datepicker was placed in the . Now this is a lib option. Unfortunately, the option is broken (I don't know why they broke it since it worked before the lib update...). As the lib does not expose its internal functions, the only found workaround was to patch the lib code itself. opw-745311 opw-745625 --- .../src/js/bootstrap-datetimepicker.js | 6 ++++++ addons/web/static/src/js/widgets/date_picker.js | 1 + addons/web/static/src/less/webclient.less | 1 + 3 files changed, 8 insertions(+) diff --git a/addons/web/static/lib/bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js b/addons/web/static/lib/bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js index 807bdff70dd..8f0724bbb00 100644 --- a/addons/web/static/lib/bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js +++ b/addons/web/static/lib/bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js @@ -421,6 +421,12 @@ element.children().first().after(widget); } + // !! ODOO FIX START !! + var parentOffset = parent.offset(); + position.top = offset.top - parentOffset.top; + position.left = offset.left - parentOffset.left; + // !! ODOO FIX END !! + // Top and bottom logic if (vertical === 'auto') { if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() && diff --git a/addons/web/static/src/js/widgets/date_picker.js b/addons/web/static/src/js/widgets/date_picker.js index bd9416a278a..b148473413c 100644 --- a/addons/web/static/src/js/widgets/date_picker.js +++ b/addons/web/static/src/js/widgets/date_picker.js @@ -39,6 +39,7 @@ var DateWidget = Widget.extend({ locale : moment.locale(), allowInputToggle: true, keyBinds: null, + widgetParent: 'body', }); }, start: function() { diff --git a/addons/web/static/src/less/webclient.less b/addons/web/static/src/less/webclient.less index 2f9878fbdc3..5e84d74b599 100644 --- a/addons/web/static/src/less/webclient.less +++ b/addons/web/static/src/less/webclient.less @@ -12,6 +12,7 @@ a { // General // ------------------------------------------------------------------ .o_web_client { + position: relative; // normally useless but required by bootstrap-datepicker background-color: @odoo-webclient-background-color; } .o_text_overflow { From 506ac455376d605bc360764ae958835b3da195e9 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 7 Jun 2017 11:24:44 +0200 Subject: [PATCH 08/24] [FIX] resource: get working hours - Use the demo resource calendar (40h/week) - Create a project forecast for the Administrator - Set the 'Start date' and 'End date' to 2017-07-07 - Plan 8 hours The % of time should be 100 %, but it's not always the case. The core of the issue comes from `get_working_hours`. Case 1: Call with start_dt = end_dt = 2017-07-07 00:00:00 `get_working_hours_of_date` is called twice with the following start/end dt: - 2017-06-07 00:00:00, 2017-06-07 00:00:00 => 0 hours - 2017-06-08 00:00:00, 2017-06-08 23:59:59.999999 => 8 hours This is obviously not correct, we don't care about what happens on 2017-06-08. The number of hours is correct in total, but it comes from the wrong day. Case 2: Call with start_dt = 2017-07-07 00:00:00 and end_dt = 2017-07-07 23:59:59 `get_working_hours_of_date` is called twice with the following start/end dt: - 2017-06-07 00:00:00, 2017-06-07 23:59:59.999999 => 8 hours - 2017-06-08 00:00:00, 2017-06-08 23:59:59.999999 => 8 hours This is obviously not correct either... We change how the `until` parameter is computed. This way, the correct call is the second call (Case 2), and `get_working_hours_of_date` is calld once with the following start/end dt: - 2017-06-07 00:00:00, 2017-06-07 23:59:59.999999 => 8 hours opw-745253 --- addons/resource/models/resource.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/resource/models/resource.py b/addons/resource/models/resource.py index 5551d1c6838..cb0270b7bfa 100644 --- a/addons/resource/models/resource.py +++ b/addons/resource/models/resource.py @@ -369,7 +369,7 @@ class ResourceCalendar(models.Model): resource_id=None, default_interval=None): hours = 0.0 for day in rrule.rrule(rrule.DAILY, dtstart=start_dt, - until=(end_dt + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0), + until=end_dt.replace(hour=23, minute=59, second=59, microsecond=999999), byweekday=self.get_weekdays()): day_start_dt = day.replace(hour=0, minute=0, second=0, microsecond=0) if start_dt and day.date() == start_dt.date(): From 65e49baedeeab52ad9ba9c1e001449527118197a Mon Sep 17 00:00:00 2001 From: Richard Mathot Date: Wed, 7 Jun 2017 16:09:42 +0200 Subject: [PATCH 09/24] [DOC] website_event_sale: think multicurrency when writing tests --- .../website_event_sale/static/src/js/website.tour.event_sale.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js index de23a9b4b67..327afe03a56 100644 --- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -75,7 +75,7 @@ tour.register('event_buy_tickets', { trigger: 'a[href="/shop/confirm_order"]:contains("Confirm")', }, { - content: "Check that the subtotal is 5,500.00", + content: "Check that the subtotal is 5,500.00 USD", // this test will fail if the currency of the main company is not USD trigger: '#order_total_untaxed .oe_currency_value:contains("5,500.00")', run: function () {}, // it's a check }, From c6b9b69f6a483e7ccebf0b823240b71a8f426fd3 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 7 Jun 2017 22:46:25 +0200 Subject: [PATCH 10/24] [FIX] base_import_module: allow selective access to module files As a consequence of rev. 76cd8d2558d2a1fc11681dbc4a134ba06fb698c0, imported modules were unable to access their resource files during import. Rather than further modifying the file_open API to whitelist paths (the whole thing needs a redesign in master), we temporarily whitelist the temporary directory by including it in the global addons_paths, making sure to undo it afterwards. This gives all lower level function access the resource files via file_open, without having to pass around whitelisted paths through many different calls. --- addons/base_import_module/models/ir_module.py | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/addons/base_import_module/models/ir_module.py b/addons/base_import_module/models/ir_module.py index e81035bda31..b8be8f11865 100644 --- a/addons/base_import_module/models/ir_module.py +++ b/addons/base_import_module/models/ir_module.py @@ -96,18 +96,23 @@ class view(osv.osv): raise osv.except_osv(_('Error !'), msg % zf.filename) with openerp.tools.osutil.tempdir() as module_dir: - z.extractall(module_dir) - dirs = [d for d in os.listdir(module_dir) if os.path.isdir(opj(module_dir, d))] - for mod_name in dirs: - module_names.append(mod_name) - try: - # assert mod_name.startswith('theme_') - path = opj(module_dir, mod_name) - self.import_module(cr, uid, mod_name, path, force=force, context=context) - success.append(mod_name) - except Exception, e: - _logger.exception('Error while importing module') - errors[mod_name] = tools.ustr(e) + import openerp.modules as addons + try: + addons.module.ad_paths.append(module_dir) + z.extractall(module_dir) + dirs = [d for d in os.listdir(module_dir) if os.path.isdir(opj(module_dir, d))] + for mod_name in dirs: + module_names.append(mod_name) + try: + # assert mod_name.startswith('theme_') + path = opj(module_dir, mod_name) + self.import_module(cr, uid, mod_name, path, force=force, context=context) + success.append(mod_name) + except Exception, e: + _logger.exception('Error while importing module') + errors[mod_name] = tools.ustr(e) + finally: + addons.module.ad_paths.remove(module_dir) r = ["Successfully imported module '%s'" % mod for mod in success] for mod, error in errors.items(): r.append("Error while importing module '%s': %r" % (mod, error)) From 3d1fe33a362422b3df8ece8b94a896379c3f4071 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Thu, 8 Jun 2017 10:17:01 +0200 Subject: [PATCH 11/24] [FIX] calendar: _get_recurrent_date_by_event Let's create in google calendar a reccurent event with all day set and a end date. When synchronising google calendar with Odoo(by clicking on button "Sync with Google"). It raised on error because the start date had a time zone but not the end date(the UNTIL in the rrule). Fine tuning of 20ed57f874176161a043dee834b25c7f0b3dc175 opw:746549 --- addons/calendar/models/calendar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index 8c18fe2e24b..145614fc045 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -553,7 +553,7 @@ class Meeting(models.Model): if not event_date: event_date = datetime.now() - if self.allday and self.rrule and 'Z' not in self.rrule: + if self.allday and self.rrule and 'UNTIL' in self.rrule and 'Z' not in self.rrule: rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date.replace(tzinfo=None), forceset=True, ignoretz=True) else: # Convert the event date to saved timezone (or context tz) as it'll From b4863378efa6a30acc507fd05dc977195c3ff254 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 8 Jun 2017 10:23:37 +0200 Subject: [PATCH 12/24] [FIX] website, website_sale: product website price and pricelist without request Attempting to read the field `website_price` directly with the XMLRPC API failed because, in such a case, the request is unbound, and attempting to read anything from it (`request.session` whatever) resulted to the raise of an exception. e.g. This prevented to perform a `search_read` with all fields on the `product.product` model Closes #14939 opw-745733 --- addons/website/models/website.py | 5 +++-- addons/website_sale/models/sale_order.py | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 0a64d587bcb..6145fe13132 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -392,9 +392,10 @@ class Website(models.Model): @api.model def get_current_website(self): - domain_name = request.httprequest.environ.get('HTTP_HOST', '').split(':')[0] + domain_name = request and request.httprequest.environ.get('HTTP_HOST', '').split(':')[0] or None website_id = self._get_current_website_id(domain_name) - request.context = dict(request.context, website_id=website_id) + if request: + request.context = dict(request.context, website_id=website_id) return self.browse(website_id) @tools.cache('domain_name') diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 59b6432e3a6..6f31edc7a68 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -244,19 +244,19 @@ class Website(models.Model): :param bool show_visible: if True, we don't display pricelist where selectable is False (Eg: Code promo) :returns: pricelist recordset """ - website = request.website - if not request.website: + website = request and request.website or None + if not website: if self.env.context.get('website_id'): website = self.browse(self.env.context['website_id']) else: website = self.search([], limit=1) - isocountry = request.session.geoip and request.session.geoip.get('country_code') or False + isocountry = request and request.session.geoip and request.session.geoip.get('country_code') or False partner = self.env.user.partner_id order_pl = partner.last_website_so_id and partner.last_website_so_id.state == 'draft' and partner.last_website_so_id.pricelist_id partner_pl = partner.property_product_pricelist pricelists = website._get_pl_partner_order(isocountry, show_visible, website.user_id.sudo().partner_id.property_product_pricelist.id, - request.session.get('website_sale_current_pl'), + request and request.session.get('website_sale_current_pl') or None, website.pricelist_ids, partner_pl=partner_pl and partner_pl.id or None, order_pl=order_pl and order_pl.id or None) @@ -280,7 +280,7 @@ class Website(models.Model): available_pricelists = self.get_pricelist_available() pl = None partner = self.env.user.partner_id - if request.session.get('website_sale_current_pl'): + if request and request.session.get('website_sale_current_pl'): # `website_sale_current_pl` is set only if the user specifically chose it: # - Either, he chose it from the pricelist selection # - Either, he entered a coupon code From 11d6b5e15e812b81d3b136113ab8278137fe01b0 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 8 Jun 2017 11:13:20 +0200 Subject: [PATCH 13/24] [FIX] account: name search on journal with negative operators In the journal items, when performing a search "Journal" "Doesn't contain" "Something" Both its name AND its code must not contain "Something". Therefore, with negative operator, the domain must not be built with a OR connector but with an AND. Same thing goes for "is equal" and "is not equal". opw-726029 --- addons/account/models/account.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/account/models/account.py b/addons/account/models/account.py index cf4d47a7425..6a2e73160bd 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -510,7 +510,10 @@ class AccountJournal(models.Model): @api.model def name_search(self, name='', args=None, operator='ilike', limit=100): args = args or [] - recs = self.search(['|', ('code', operator, name), ('name', operator, name)] + args, limit=limit) + connector = '|' + if operator in expression.NEGATIVE_TERM_OPERATORS: + connector = '&' + recs = self.search([connector, ('code', operator, name), ('name', operator, name)] + args, limit=limit) return recs.name_get() @api.multi From 8052869b19aaf2105713e0a8ba0555ca25dc26ef Mon Sep 17 00:00:00 2001 From: nafex Date: Tue, 6 Jun 2017 15:25:42 +0200 Subject: [PATCH 14/24] [FIX] delivery: create_price_rules won't create unnecessary rules anymore When updating a carrier that is not "fixed" or "base_on_rule", this method was creating unnecessary delivery.price.rule at each write. This patch fixes this issue. Closes PR #17443 --- addons/delivery/models/delivery_carrier.py | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py index dad4e8a315f..fa46ef9b5f3 100644 --- a/addons/delivery/models/delivery_carrier.py +++ b/addons/delivery/models/delivery_carrier.py @@ -201,26 +201,26 @@ class DeliveryCarrier(models.Model): if record.delivery_type == 'fixed': PriceRule.search([('carrier_id', '=', record.id)]).unlink() - line_data = { - 'carrier_id': record.id, - 'variable': 'price', - 'operator': '>=', - } - # Create the delivery price rules - if record.free_if_more_than: - line_data.update({ - 'max_value': record.amount, - 'standard_price': 0.0, - 'list_base_price': 0.0, - }) - PriceRule.create(line_data) - if record.fixed_price is not False: - line_data.update({ - 'max_value': 0.0, - 'standard_price': record.fixed_price, - 'list_base_price': record.fixed_price, - }) - PriceRule.create(line_data) + line_data = { + 'carrier_id': record.id, + 'variable': 'price', + 'operator': '>=', + } + # Create the delivery price rules + if record.free_if_more_than: + line_data.update({ + 'max_value': record.amount, + 'standard_price': 0.0, + 'list_base_price': 0.0, + }) + PriceRule.create(line_data) + if record.fixed_price is not False: + line_data.update({ + 'max_value': 0.0, + 'standard_price': record.fixed_price, + 'list_base_price': record.fixed_price, + }) + PriceRule.create(line_data) return True @api.model From 1612f2cdf797adb1e7639874c8d0138d0d939749 Mon Sep 17 00:00:00 2001 From: xmo-odoo Date: Thu, 8 Jun 2017 11:42:25 +0200 Subject: [PATCH 15/24] [FIX] website_forum: untranslatable strings OPW-746261 --- addons/website_forum/i18n/website_forum.pot | 41 ++++++++++++++++++++ addons/website_forum/views/website_forum.xml | 38 ++++++++++-------- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/addons/website_forum/i18n/website_forum.pot b/addons/website_forum/i18n/website_forum.pot index effc13bfb14..1d4b4a567db 100644 --- a/addons/website_forum/i18n/website_forum.pot +++ b/addons/website_forum/i18n/website_forum.pot @@ -604,6 +604,7 @@ msgid "Bad Request" msgstr "" #. module: website_forum +#: model:ir.model.fields,field_description:website_forum.field_res_users_badge_ids #: model:ir.ui.view,arch_db:website_forum.badge #: model:ir.ui.view,arch_db:website_forum.header #: model:ir.ui.view,arch_db:website_forum.user_detail_full @@ -782,6 +783,7 @@ msgstr "" #. openerp-web #: code:addons/website_forum/static/src/xml/website_forum_share_templates.xml:46 #: selection:forum.post,state:0 +#: model:ir.ui.view,arch_db:website_forum.post_description_full #, python-format msgid "Close" msgstr "" @@ -883,6 +885,16 @@ msgstr "" msgid "Convert all answers to comments and vice versa" msgstr "" +#. module: website_forum +#: model:ir.ui.view,arch_db:website_forum.post_answers +msgid "Convert as a comment" +msgstr "" + +#. module: website_forum +#: model:ir.ui.view,arch_db:website_forum.post_comment +msgid "Convert as an answer" +msgstr "" + #. module: website_forum #: model:ir.model.fields,field_description:website_forum.field_forum_post_karma_accept msgid "Convert comment to answer" @@ -968,6 +980,12 @@ msgstr "" msgid "Default Post" msgstr "" +#. module: website_forum +#: model:ir.ui.view,arch_db:website_forum.post_answers +#: model:ir.ui.view,arch_db:website_forum.post_description_full +msgid "Delete" +msgstr "" + #. module: website_forum #: model:ir.model.fields,field_description:website_forum.field_forum_forum_karma_unlink_all msgid "Delete all posts" @@ -1038,6 +1056,8 @@ msgstr "" #. module: website_forum #: model:ir.ui.view,arch_db:website_forum.edit_post +#: model:ir.ui.view,arch_db:website_forum.post_answers +#: model:ir.ui.view,arch_db:website_forum.post_description_full msgid "Edit" msgstr "" @@ -1184,6 +1204,11 @@ msgstr "" msgid "First upvote" msgstr "" +#. module: website_forum +#: model:ir.ui.view,arch_db:website_forum.post_description_full +msgid "Flag" +msgstr "" + #. module: website_forum #: model:ir.model.fields,field_description:website_forum.field_forum_forum_karma_flag #: model:ir.model.fields,field_description:website_forum.field_forum_post_karma_flag @@ -1193,6 +1218,7 @@ msgstr "" #. module: website_forum #: selection:forum.post,state:0 #: model:ir.ui.view,arch_db:website_forum.moderation_queue +#: model:ir.ui.view,arch_db:website_forum.post_description_full msgid "Flagged" msgstr "" @@ -2012,6 +2038,11 @@ msgstr "" msgid "Re: %s" msgstr "" +#. module: website_forum +#: model:ir.ui.view,arch_db:website_forum.post_description_full +msgid "Reactivate" +msgstr "" + #. module: website_forum #: model:ir.ui.view,arch_db:website_forum.header msgid "Read Guidelines" @@ -2064,6 +2095,11 @@ msgstr "" msgid "Relevance Computation" msgstr "" +#. module: website_forum +#: model:ir.ui.view,arch_db:website_forum.post_description_full +msgid "Reopen" +msgstr "" + #. module: website_forum #: model:ir.ui.view,arch_db:website_forum.post_answers #: model:ir.ui.view,arch_db:website_forum.post_description_full @@ -2384,6 +2420,11 @@ msgstr "" msgid "This post is already flagged" msgstr "" +#. module: website_forum +#: model:ir.ui.view,arch_db:website_forum.post_description_full +msgid "This post is currently awaiting moderation and not yet published... Do you want Accept or Reject this post ?" +msgstr "" + #. module: website_forum #: model:ir.ui.view,arch_db:website_forum.private_profile msgid "This profile is private!" diff --git a/addons/website_forum/views/website_forum.xml b/addons/website_forum/views/website_forum.xml index 06692cc9b2b..5cfeef0670c 100644 --- a/addons/website_forum/views/website_forum.xml +++ b/addons/website_forum/views/website_forum.xml @@ -787,9 +787,10 @@
- - - + + + + By - + Reopen @@ -826,7 +827,7 @@
  • - + Reopen @@ -834,7 +835,7 @@
  • - + Edit @@ -842,7 +843,7 @@
  • - + Close @@ -850,7 +851,7 @@
  • - + Delete @@ -858,7 +859,7 @@
  • - + Reactivate @@ -866,7 +867,7 @@
  • - + Reactivate @@ -877,7 +878,12 @@ t-attf-data-karma="#{not question.can_flag and question.forum_id.karma_flag or 0}" t-attf-data-href="/forum/#{slug(forum)}/post/#{slug(question)}/flag" > - + + Flagged + + + Flag +
  • @@ -964,7 +970,7 @@
  • - + Edit @@ -972,7 +978,7 @@
  • - + Delete @@ -989,7 +995,7 @@
  • - + Convert as a comment @@ -1057,7 +1063,7 @@ - + @@ -1070,7 +1076,7 @@ - + Convert as an answer From c4c9347b9986f60f1b8a534b805e248b25e3eef8 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Thu, 8 Jun 2017 13:53:49 +0200 Subject: [PATCH 16/24] [IMP] web: Expose char_domain dialog widget This simple return allows submodules to be able to know when a dialog is shown and modify something in it. Note from GED: I am aware that this is a IMP in a stable version, and I really don't like that... But it looks like it really helps many people, as shown by the PR, and the risk induced by this commit is definitely extremely low, so I will make an exception. (PR: #15579) --- addons/web/static/src/js/views/form_widgets.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js index 450025ee06b..70a11214901 100644 --- a/addons/web/static/src/js/views/form_widgets.js +++ b/addons/web/static/src/js/views/form_widgets.js @@ -501,6 +501,8 @@ var FieldCharDomain = common.AbstractField.extend(common.ReinitializeFieldMixin, } } }).open(); + this.trigger("dialog_opened", dialog); + return dialog; }, }); From 77be3d8b396f31adfa11d2cd10a22ee3a7f27883 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 7 Jun 2017 15:49:32 +0200 Subject: [PATCH 17/24] [FIX] account: avoid duplicated move name Commits 9365482df05 and 8fc81b871debc2 attempted to prevent the creation of two moves with the same name during reconciliation. It works in most cases, but it is still possible to reconcile the same statement line with more than one line. We add an extra check to make sure duplicate never happen. opw-742018 --- addons/account/models/account_bank_statement.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py index c9634d5f270..e49546e31e7 100644 --- a/addons/account/models/account_bank_statement.py +++ b/addons/account/models/account_bank_statement.py @@ -891,6 +891,10 @@ class AccountBankStatementLine(models.Model): # Create the move self.sequence = self.statement_id.line_ids.ids.index(self.id) + 1 move_name = (self.statement_id.name or self.name) + "/" + str(self.sequence) + # Ensure no duplicate is created + if self.env['account.move'].search([('name', '=', move_name)]): + count = self.env['account.move'].search_count([('name', '=like', move_name + '/%')]) + move_name = u'{}/{}'.format(move_name, count + 1) move_vals = self._prepare_reconciliation_move(move_name) move = self.env['account.move'].create(move_vals) counterpart_moves = (counterpart_moves | move) From 45e94ec756e0da4766110bd3ce94ede4203e4486 Mon Sep 17 00:00:00 2001 From: xmo-odoo Date: Thu, 8 Jun 2017 14:16:43 +0200 Subject: [PATCH 18/24] [FIX] web: CrashManager can crash if trying to show an empty message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If `d.message` is an empty string, `!!d.message` is false and the crashmanager will try to display `d.error.data.message`… which does not exist as it's a warning with an empty message not an upstream error. That causes the crashmanager itself to crash, and even the warning name/title to be lost. --- addons/web/static/src/xml/base.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index c846e969827..161509b364f 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -22,7 +22,7 @@
    - var message = d.message ? d.message : d.error.data.message; + var message = (d.message !== undefined) ? d.message : d.error.data.message; d.html_error = context.engine.tools.html_escape(message).replace(/\n/g, '
    ');
    From fdc87498880b19754faf7fc57f456d6383bfbbf7 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Thu, 8 Jun 2017 15:41:07 +0200 Subject: [PATCH 19/24] [FIX] crm, website_crm_partner_assign: recompute saleteam automatically Add a constrains on user_id field to avoid to recompute manually the team_id. Eg: action_foward didn't call the onchange manually, so the saleteam not updated. --- addons/crm/models/crm_lead.py | 1 + addons/website_crm_partner_assign/models/crm_lead.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index a7f4ff77290..e9cf8f9d372 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -249,6 +249,7 @@ class Lead(models.Model): team_id = self.env['crm.team']._get_default_team_id(user_id=user_id) return {'team_id': team_id} + @api.constrains('user_id') @api.onchange('user_id') def _onchange_user_id(self): """ When changing the user, also set a team_id or restrict team id to the ones user_id is member of. """ diff --git a/addons/website_crm_partner_assign/models/crm_lead.py b/addons/website_crm_partner_assign/models/crm_lead.py index 6556deeec70..f5d59edbd8c 100644 --- a/addons/website_crm_partner_assign/models/crm_lead.py +++ b/addons/website_crm_partner_assign/models/crm_lead.py @@ -47,8 +47,6 @@ class CrmLead(models.Model): for salesman_id, leads_ids in salesmans_leads.items(): leads = self.browse(leads_ids) leads.write({'user_id': salesman_id}) - for lead in leads: - lead._onchange_user_id() @api.multi def action_assign_partner(self): From e79eaecc46d72187c8a7ad39c0f2b17b61dff267 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Thu, 8 Jun 2017 15:37:55 +0200 Subject: [PATCH 20/24] [FIX] stock:Creating new lots when option "use only existing lots" is activated When the field use_create_lots is set to False in a picking type, it is not possible to create lot for a pack operation linked to this picking type. opw:744862 --- addons/stock/i18n/stock.pot | 6 ++++++ addons/stock/models/stock_production_lot.py | 10 ++++++++++ addons/stock/views/stock_pack_operation_views.xml | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/addons/stock/i18n/stock.pot b/addons/stock/i18n/stock.pot index e01b6291165..c257035f4a9 100644 --- a/addons/stock/i18n/stock.pot +++ b/addons/stock/i18n/stock.pot @@ -6596,6 +6596,12 @@ msgid "You cannot set a negative product quantity in an inventory line:\n" " %s - qty: %s" msgstr "" +#. module: stock +#: code:addons/stock/models/stock_production_lot.py:37 +#, python-format +msgid "You are not allowed to create a lot for this picking type" +msgstr "" + #. module: stock #: code:addons/stock/models/stock_move.py:919 #, python-format diff --git a/addons/stock/models/stock_production_lot.py b/addons/stock/models/stock_production_lot.py index 60f370fff51..a568cd80fe3 100644 --- a/addons/stock/models/stock_production_lot.py +++ b/addons/stock/models/stock_production_lot.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models, _ +from odoo.exceptions import UserError class ProductionLot(models.Model): @@ -27,6 +28,15 @@ class ProductionLot(models.Model): ('name_ref_uniq', 'unique (name, product_id)', 'The combination of serial number and product must be unique !'), ] + @api.model + def create(self, vals): + pack_id = self.env.context.get('active_pack_operation', False) + if pack_id: + pack = self.env['stock.pack.operation'].browse(pack_id) + if pack.picking_id and not pack.picking_id.picking_type_id.use_create_lots: + raise UserError(_("You are not allowed to create a lot for this picking type")) + return super(ProductionLot, self).create(vals) + @api.one @api.depends('quant_ids.qty') def _product_qty(self): diff --git a/addons/stock/views/stock_pack_operation_views.xml b/addons/stock/views/stock_pack_operation_views.xml index 5b6919dab83..cd8469d4672 100644 --- a/addons/stock/views/stock_pack_operation_views.xml +++ b/addons/stock/views/stock_pack_operation_views.xml @@ -85,7 +85,7 @@ + context="{'default_product_id': parent.product_id, 'active_pack_operation': parent.id}"/>