From de3ff2e53373e7200bd123d58edb32fcfca9fccf Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Fri, 15 Mar 2019 13:25:11 +0000 Subject: [PATCH 01/58] [FIX] analytic: account search no partner=>work ok The name_search of account.analytic.account do not find records that have no partner set. This is because some ORM search may breaks when it contains a auto_join relational field. We will try to fix this (at least in master version). In this changeset, we avoid the issue by combining 2 searches instead. opw-1941547 opw-1950010 closes #31881 Signed-off-by: Nicolas Lempereur (nle) --- addons/analytic/models/analytic_account.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/analytic/models/analytic_account.py b/addons/analytic/models/analytic_account.py index bc88e94a19e07..68c22417da290 100644 --- a/addons/analytic/models/analytic_account.py +++ b/addons/analytic/models/analytic_account.py @@ -160,7 +160,10 @@ def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_ui if operator == 'ilike' and not (name or '').strip(): domain = [] else: - domain = ['|', '|', ('code', operator, name), ('name', operator, name), ('partner_id.name', operator, name)] + # search by partner separately because auto_join field can break searches + partner_domain = [('partner_id.name', operator, name)] + ids_partner = self._search(expression.AND([partner_domain, args]), limit=limit, access_rights_uid=name_get_uid) + domain = ['|', '|', ('code', operator, name), ('name', operator, name), ('id', 'in', ids_partner)] analytic_account_ids = self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid) return self.browse(analytic_account_ids).name_get() From 8003289219b2babc184aba55445f96b99a71c0d9 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Thu, 14 Mar 2019 13:10:49 +0000 Subject: [PATCH 02/58] [FIX] mail: activity document name=>updated name The activities "Document Name" (res_name) are computed and stored by getting them on original document, so if we change name of original model => the activity names are not updated. Thus the document name we see we showing the "Activity" view is the one that was used when activities were created with an odd heuristic (by inverse order of activity type and max alphabetical value) and not translated. With this changeset we ignore "Document Name" and directly get names of records when display "Activity" view. note: the changeset also filter out activities on document we can reads: this was done when a domain was set but not otherwise so access error could be easily gotten. opw-1949437 closes #31850 Signed-off-by: Nicolas Lempereur (nle) --- addons/mail/models/mail_activity.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py index 0b327d5933862..982d67b21c7e7 100644 --- a/addons/mail/models/mail_activity.py +++ b/addons/mail/models/mail_activity.py @@ -434,19 +434,21 @@ def get_activity_data(self, res_model, domain): activity_domain.append(('res_id', 'in', res.ids)) grouped_activities = self.env['mail.activity'].read_group( activity_domain, - ['res_id', 'activity_type_id', 'res_name:max(res_name)', 'ids:array_agg(id)', 'date_deadline:min(date_deadline)'], + ['res_id', 'activity_type_id', 'ids:array_agg(id)', 'date_deadline:min(date_deadline)'], ['res_id', 'activity_type_id'], lazy=False) + # filter out unreadable records + if not domain: + res_ids = tuple(a['res_id'] for a in grouped_activities) + res = self.env[res_model].search([('id', 'in', res_ids)]) + grouped_activities = [a for a in grouped_activities if a['res_id'] in res.ids] activity_type_ids = self.env['mail.activity.type'] - res_id_to_name = {} res_id_to_deadline = {} activity_data = defaultdict(dict) for group in grouped_activities: res_id = group['res_id'] - res_name = group['res_name'] activity_type_id = group['activity_type_id'][0] activity_type_ids |= self.env['mail.activity.type'].browse(activity_type_id) # we will get the name when reading mail_template_ids - res_id_to_name[res_id] = res_name res_id_to_deadline[res_id] = group['date_deadline'] if (res_id not in res_id_to_deadline or group['date_deadline'] < res_id_to_deadline[res_id]) else res_id_to_deadline[res_id] state = self._compute_state_from_date(group['date_deadline'], self.user_id.sudo().tz) activity_data[res_id][activity_type_id] = { @@ -456,6 +458,7 @@ def get_activity_data(self, res_model, domain): 'o_closest_deadline': group['date_deadline'], } res_ids_sorted = sorted(res_id_to_deadline, key=lambda item: res_id_to_deadline[item]) + res_id_to_name = dict(self.env[res_model].browse(res_ids_sorted).name_get()) activity_type_infos = [] for elem in sorted(activity_type_ids, key=lambda item: item.sequence): mail_template_info = [] From 716ff76d5e639654171a8795461282f3e80a74dc Mon Sep 17 00:00:00 2001 From: Julien Castiaux Date: Tue, 19 Mar 2019 15:28:24 +0000 Subject: [PATCH 03/58] [FIX] sale: force maximum section/subtotal width Step to reproduce: - Install sale (sale_management) and studio (web_studio) - Change the document template (settings > configuration > general settings) to select the second one - Go to an order form - Enable studio, select "Reports" in the right tab - Edit the "Quotation/Order" report, add a new colomn - Back to the order form, enable dev mode and add a section to the order lines. - Print the report => the section is not using all the available place. The section/subtotal are using a colspan to use all the place in the table, this colspan is hardcoded to 5, the number of columns without customization. Adding a new column doesn't change that value. The fix has been to force the colspan at 99, the maximum value for colspans. Using that value will always render the row to fill the table. opw-1936088 closes odoo/odoo#31951 Signed-off-by: Julien Castiaux --- addons/sale/report/sale_report_templates.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/sale/report/sale_report_templates.xml b/addons/sale/report/sale_report_templates.xml index b438cbd3db2d3..edce182dcfc2a 100644 --- a/addons/sale/report/sale_report_templates.xml +++ b/addons/sale/report/sale_report_templates.xml @@ -116,14 +116,14 @@ - + - + @@ -131,7 +131,7 @@ - + Subtotal Date: Tue, 19 Mar 2019 10:30:34 +0000 Subject: [PATCH 04/58] [FIX] stock_dropshipping: modify SO - Create a SO with a dropship product, set a qty of 1.0 - Validate the SO - Validate the corresponding PO - Change the qty to the SO to 2.0 2 PO are created: the first one with a qty of 1.0, another with a qty of 3.0. There is an overlap of functionality between: - `_purchase_increase_ordered_qty` from `sale_purchase` (first PO) - `_action_launch_stock_rule` from `sale_stock` (second PO) The quantity is incorrectly computed since the quantity on the PO is taken into account only if the no stock move exist. We remove the latter condition, so the procurement quantity is computed correctly. opw-1950076 closes odoo/odoo#31963 Signed-off-by: Nicolas Martinelli (nim) --- addons/stock_dropshipping/models/sale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock_dropshipping/models/sale.py b/addons/stock_dropshipping/models/sale.py index b527701a04985..c942e5cdca158 100644 --- a/addons/stock_dropshipping/models/sale.py +++ b/addons/stock_dropshipping/models/sale.py @@ -13,7 +13,7 @@ class SaleOrderLine(models.Model): def _get_qty_procurement(self): # People without purchase rights should be able to do this operation purchase_lines_sudo = self.sudo().purchase_line_ids - if not self.move_ids.filtered(lambda r: r.state != 'cancel') and purchase_lines_sudo.filtered(lambda r: r.state != 'cancel'): + if purchase_lines_sudo.filtered(lambda r: r.state != 'cancel'): qty = 0.0 for po_line in purchase_lines_sudo.filtered(lambda r: r.state != 'cancel'): qty += po_line.product_uom._compute_quantity(po_line.product_qty, self.product_uom, rounding_method='HALF-UP') From 577333f5485723636c81db7e13bfab3af7d95620 Mon Sep 17 00:00:00 2001 From: Jorge Pinna Puissant Date: Tue, 19 Mar 2019 14:33:07 +0000 Subject: [PATCH 05/58] [FIX] account: invoice alias in multi company Install accounting in a multi company environment. Configure email server. Configure an alias for the vendor bill journal per company. Before this commit, all the invoices were created in the same company, regardless of the alias used. Now, the invoice is created in the company of the used alias. opw-1939909 closes odoo/odoo#31948 Signed-off-by: Nicolas Martinelli (nim) --- addons/account/models/account.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 00bdabb7f9e5e..c4569e918867c 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -580,8 +580,7 @@ def _get_alias_values(self, alias_name=None): if self.company_id != self.env.ref('base.main_company'): alias_name += '-' + str(self.company_id.name) return { - 'alias_defaults': {'type': 'in_invoice'}, - 'alias_user_id': self.env.user.id, + 'alias_defaults': {'type': 'in_invoice', 'company_id': self.company_id.id}, 'alias_parent_thread_id': self.id, 'alias_name': re.sub(r'[^\w]+', '-', alias_name) } From f7acbf6466a712dd086d13b14a69f415afe7dc13 Mon Sep 17 00:00:00 2001 From: Alex Tuyls Date: Tue, 19 Mar 2019 15:57:46 +0000 Subject: [PATCH 06/58] [FIX] website_event,website_event_sale: correction pricelist in event register A recent fix c41d1d2 was made to select the related pricelist to show the correct price of an event product. This fix creates another bug in case of Ecommerce app not installed, because the method get_current_pricelist is defined in website_sale module. We need to move the fix from website_event to website_event_sale module. closes odoo/odoo#31953 Signed-off-by: Jorge Pinna Puissant (jpp) --- addons/website_event/controllers/main.py | 5 +---- addons/website_event_sale/controllers/main.py | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index 9e86e2ddebab3..acff56f932156 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -181,10 +181,7 @@ def event(self, event, **post): def event_register(self, event, **post): if not event.can_access_from_current_website(): raise werkzeug.exceptions.NotFound() - if not request.context.get('pricelist'): - pricelist = request.website.get_current_pricelist() - if pricelist: - event = event.with_context(pricelist=pricelist.id) + values = { 'event': event, 'main_object': event, diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index 689fe5cdc81ac..acf5f58be2d98 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -11,6 +11,10 @@ class WebsiteEventSaleController(WebsiteEventController): @http.route() def event_register(self, event, **post): event = event.with_context(pricelist=request.website.id) + if not request.context.get('pricelist'): + pricelist = request.website.get_current_pricelist() + if pricelist: + event = event.with_context(pricelist=pricelist.id) return super(WebsiteEventSaleController, self).event_register(event, **post) def _process_tickets_details(self, data): From 1d967879293b8ddf3d6b10b3c4f8fd3d49e3529c Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 20 Mar 2019 10:01:19 +0000 Subject: [PATCH 07/58] [FIX] purchase_requisition: PO date When creating a new PO from a purchase requisition, the order date of the PO is the last date of the contract. There is no reason for this, and althoguh the date can be manually changed, it is error-prone. opw-1937141 closes odoo/odoo#31969 Signed-off-by: Nicolas Martinelli (nim) --- addons/purchase_requisition/models/purchase_requisition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/purchase_requisition/models/purchase_requisition.py b/addons/purchase_requisition/models/purchase_requisition.py index 18d13b99d6e65..3ede7e303b6f9 100644 --- a/addons/purchase_requisition/models/purchase_requisition.py +++ b/addons/purchase_requisition/models/purchase_requisition.py @@ -326,7 +326,7 @@ def _onchange_requisition_id(self): else: self.origin = requisition.name self.notes = requisition.description - self.date_order = requisition.date_end or fields.Datetime.now() + self.date_order = fields.Datetime.now() self.picking_type_id = requisition.picking_type_id.id if requisition.type_id.line_copy != 'copy': From f96791051d4f8247213b14c9f1d5d6cbc1a62c7a Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Wed, 13 Mar 2019 12:46:54 +0000 Subject: [PATCH 08/58] [FIX] stock: lot action => str to prevent traceback In a report a lot could be opened with an action that was a number instead of expected string or falsy value. In web and web_studio, we sometime expect the action name to be a string or falsy (calls to `bc.title.trim()` in breadcrumbs code). opw-1951174 closes #31816 Signed-off-by: Nicolas Lempereur (nle) --- addons/stock/static/src/js/stock_traceability_report_widgets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/static/src/js/stock_traceability_report_widgets.js b/addons/stock/static/src/js/stock_traceability_report_widgets.js index 7a52ea100c19c..97468a48135af 100644 --- a/addons/stock/static/src/js/stock_traceability_report_widgets.js +++ b/addons/stock/static/src/js/stock_traceability_report_widgets.js @@ -39,7 +39,7 @@ var ReportWidget = Widget.extend({ this.do_action({ type: 'ir.actions.client', tag: 'stock_report_generic', - name: $el.data('lot_name'), + name: $el.data('lot_name') !== undefined && $el.data('lot_name').toString(), context: { active_id : $el.data('lot_id'), active_model : 'stock.production.lot', From 7a7dbf97c661ec5ac19de72a55feb28093d78482 Mon Sep 17 00:00:00 2001 From: iledarn Date: Fri, 15 Mar 2019 06:48:56 +0000 Subject: [PATCH 09/58] [CLA] signature for Iledarn --- doc/cla/individual/iledarn.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/iledarn.md diff --git a/doc/cla/individual/iledarn.md b/doc/cla/individual/iledarn.md new file mode 100644 index 0000000000000..0f174ec032279 --- /dev/null +++ b/doc/cla/individual/iledarn.md @@ -0,0 +1,11 @@ +Russia, 2019-03-15 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Ildar Nasyrov iledarn@gmail.com https://github.com/iledarn From 4b8e0d82bb2463e9d5e3492f7014c89cdc41b5e7 Mon Sep 17 00:00:00 2001 From: Lucas Lefevre Date: Wed, 9 Jan 2019 12:32:57 +0000 Subject: [PATCH 10/58] [FIX] web: backport of 70c6d3b04c to 12.0 make sidebar filter generic Adding `res_model` and `res_fields` attributes on a field of a calendar view adds a filter in the sidebar. It saves the result as the defined model and should save it in the defined field. However `partner_id` has been hardcoded in the rpc call that creates the record. This breaks genericity, it cannot be used with another field than `partner_id`. This commit makes this generic by correctly setting the field name in the rpc call. closes odoo/odoo#31858 Signed-off-by: Martin Trigaux (mat) Co-authored-by: Ildar Nasyrov --- addons/web/static/src/js/views/calendar/calendar_renderer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js index 03c59885fbe9f..0ea7d60b1ce08 100644 --- a/addons/web/static/src/js/views/calendar/calendar_renderer.js +++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js @@ -112,11 +112,13 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, { _onFieldChanged: function (event) { var self = this; event.stopPropagation(); + var createValues = {'user_id': session.uid}; var value = event.data.changes[this.write_field].id; + createValues[this.write_field] = value; this._rpc({ model: this.write_model, method: 'create', - args: [{'user_id': session.uid,'partner_id': value,}], + args: [createValues], }) .then(function () { self.trigger_up('changeFilter', { From 215c4a6b5d71c56315f09ce388a8316b76a3a23e Mon Sep 17 00:00:00 2001 From: Nans Lefebvre Date: Tue, 5 Mar 2019 12:57:51 +0000 Subject: [PATCH 11/58] [FIX] base: check that the state matches the country at partner create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Import a list of partners with their addresses. The state_id field is usually filled with the state codes. E.g. 'CA' is used for California, but also for Cádiz (Spain), etc. The import function (db_id_for) uses a name_search on the res.country.state, and takes the first matching result. It follows that the state does not necessarily match the country. Therefore we add a _check_import_consistency in the create. Here we check that the country matches the state's country, try to find a correct match, and if we can't we put the state to False. Note that if the country is not set both fields will end up set to False: this is because only a state would mean using a code could give an abitrary country. opw 1943904 closes odoo/odoo#31599 Signed-off-by: Nans Lefebvre (len) --- odoo/addons/base/models/res_partner.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py index 47d1e795950da..93d7b175a7827 100644 --- a/odoo/addons/base/models/res_partner.py +++ b/odoo/addons/base/models/res_partner.py @@ -543,6 +543,8 @@ def write(self, vals): @api.model_create_multi def create(self, vals_list): + if self.env.context.get('import_file'): + self._check_import_consistency(vals_list) for vals in vals_list: if vals.get('website'): vals['website'] = self._clean_website(vals['website']) @@ -864,6 +866,26 @@ def get_import_templates(self): 'template': '/base/static/xls/res_partner.xls' }] + @api.model + def _check_import_consistency(self, vals_list): + """ + The values created by an import are generated by a name search, field by field. + As a result there is no check that the field values are consistent with each others. + We check that if the state is given a value, it does belong to the given country, or we remove it. + """ + States = self.env['res.country.state'] + states_ids = {vals['state_id'] for vals in vals_list if vals.get('state_id')} + state_to_country = States.search([('id', 'in', list(states_ids))]).read(['country_id']) + for vals in vals_list: + if vals.get('state_id'): + country_id = next(c['country_id'][0] for c in state_to_country if c['id'] == vals.get('state_id')) + state = States.browse(vals['state_id']) + if state.country_id.id != country_id: + state_domain = [('code', '=', state.code), + ('country_id', '=', country_id)] + state = States.search(state_domain, limit=1) + vals['state_id'] = state.id # replace state or remove it if not found + @api.multi def _get_country_name(self): return self.country_id.name or '' From f451f90b9a932c0fcbf4516f4b9368ef2d067ad1 Mon Sep 17 00:00:00 2001 From: Dipalee Bhalodia Date: Thu, 25 Oct 2018 12:43:18 +0000 Subject: [PATCH 12/58] [FIX] point_of_sale: use IoT Box instead of posbox in pos.config + explain better TaskId: #1893479 closes odoo/odoo#31747 Signed-off-by: Olivier Dony (odo) --- addons/point_of_sale/views/pos_config_view.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml index 0a5a334d16907..fde48ebc2f545 100644 --- a/addons/point_of_sale/views/pos_config_view.xml +++ b/addons/point_of_sale/views/pos_config_view.xml @@ -109,16 +109,16 @@ -

IotBox / Hardware Proxy

+

IoT Box

-