diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index be5da8ba481..ee5eae3f60e 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1501,8 +1501,9 @@ class AccountMoveLine(models.Model): an analytic account. This method is intended to be extended in other modules. """ amount = (self.credit or 0.0) - (self.debit or 0.0) + default_name = self.name or (self.ref or '/' + ' -- ' + (self.partner_id and self.partner_id.name or '/')) return { - 'name': self.name, + 'name': default_name, 'date': self.date, 'account_id': self.analytic_account_id.id, 'tag_ids': [(6, 0, self._get_analytic_tag_ids())], diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js index a812057b9c4..8aaba6e373c 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js @@ -291,8 +291,12 @@ var LineRenderer = Widget.extend(FieldManagerMixin, { self.fields = { partner_id : new relational_fields.FieldMany2One(self, 'partner_id', - self.model.get(recordID), - {mode: 'edit'} + self.model.get(recordID), { + mode: 'edit', + attrs: { + placeholder: self._initialState.st_line.communication_partner_name || '', + } + } ) }; self.fields.partner_id.appendTo(self.$('.accounting_view caption')); diff --git a/addons/auth_signup/models/res_partner.py b/addons/auth_signup/models/res_partner.py index d5c78ecfb90..c0da1d85f83 100644 --- a/addons/auth_signup/models/res_partner.py +++ b/addons/auth_signup/models/res_partner.py @@ -108,6 +108,7 @@ class ResPartner(models.Model): allow_signup = self.env['ir.config_parameter'].sudo().get_param('auth_signup.invitation_scope', 'b2b') == 'b2c' for partner in self: if allow_signup and not partner.user_ids: + partner = partner.sudo() partner.signup_prepare() res[partner.id]['auth_signup_token'] = partner.signup_token elif partner.user_ids: diff --git a/addons/barcodes/static/src/js/barcode_events.js b/addons/barcodes/static/src/js/barcode_events.js index 2d602a36b97..0f2849525de 100644 --- a/addons/barcodes/static/src/js/barcode_events.js +++ b/addons/barcodes/static/src/js/barcode_events.js @@ -143,6 +143,8 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, { e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Escape" || e.key === "Tab" || e.key === "Backspace" || e.key === "Delete" || + e.key === "Home" || e.key === "End" || + e.key === "PageUp" || e.key === "PageDown" || e.key === "Unidentified" || /F\d\d?/.test(e.key)) { return true; } else { diff --git a/addons/hr_gamification/security/ir.model.access.csv b/addons/hr_gamification/security/ir.model.access.csv index 9ace96f7139..78979d89de5 100644 --- a/addons/hr_gamification/security/ir.model.access.csv +++ b/addons/hr_gamification/security/ir.model.access.csv @@ -2,4 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink challenge_officer,"Challenge Officer",gamification.model_gamification_challenge,hr.group_hr_user,1,1,1,1 challenge_line_officer,"Challenge Line Officer",gamification.model_gamification_challenge_line,hr.group_hr_user,1,1,1,1 badge_officer,"Badge Officer",gamification.model_gamification_badge,hr.group_hr_user,1,1,1,1 -badge_user_officer,"Badge-user Officer",gamification.model_gamification_badge_user,hr.group_hr_user,1,1,1,1 +badge_user_officer,"Badge-user Officer",gamification.model_gamification_badge_user,hr.group_hr_user,1,1,1,1 diff --git a/addons/pad_project/models/project.py b/addons/pad_project/models/project.py index f994b3a5ea7..6d2273b15dd 100644 --- a/addons/pad_project/models/project.py +++ b/addons/pad_project/models/project.py @@ -9,7 +9,7 @@ class ProjectTask(models.Model): _inherit = ["project.task", 'pad.common'] description_pad = fields.Char('Pad URL', pad_content_field='description', copy=False) - use_pad = fields.Boolean(related="project_id.use_pads", string="Use collaborative pad") + use_pad = fields.Boolean(related="project_id.use_pads", string="Use collaborative pad", readonly=True) @api.model def create(self, vals): diff --git a/addons/payment_stripe/static/src/js/stripe.js b/addons/payment_stripe/static/src/js/stripe.js index 529e0dc43d2..262068fd12d 100644 --- a/addons/payment_stripe/static/src/js/stripe.js +++ b/addons/payment_stripe/static/src/js/stripe.js @@ -105,7 +105,10 @@ odoo.define('payment_stripe.stripe', function(require) { acquirer_id: acquirer_id, access_token: access_token, }).then(function(data) { - try { provider_form[0].innerHTML = data; } catch (e) {}; + var $pay_stripe = $('#pay_stripe').detach(); + try { provider_form[0].innerHTML = data; } catch (e) {} + // Restore 'Pay Now' button HTML since data might have changed it. + $(provider_form[0]).find('#pay_stripe').replaceWith($pay_stripe); }).done(function () { getStripeHandler().open({ name: merchant, diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py index 6f67e4817aa..82124e4d0ec 100644 --- a/addons/sale_stock/tests/test_sale_stock.py +++ b/addons/sale_stock/tests/test_sale_stock.py @@ -423,3 +423,76 @@ class TestSaleStock(TestSale): # check the delivered quantity self.assertEqual(so1.order_line.qty_delivered, 3.0) + def test_07_forced_qties(self): + """ Make multiple sale order lines of the same product which isn't available in stock. On + the picking, create new move lines (through the detailed operations view). See that the move + lines are correctly dispatched through the moves. + """ + uom_unit = self.env.ref('product.product_uom_unit') + uom_dozen = self.env.ref('product.product_uom_dozen') + item1 = self.products['prod_order'] + + self.assertEqual(item1.uom_id.id, uom_unit.id) + + # sell a dozen + so1 = self.env['sale.order'].create({ + 'partner_id': self.partner.id, + 'order_line': [ + (0, 0, { + 'name': item1.name, + 'product_id': item1.id, + 'product_uom_qty': 1, + 'product_uom': uom_dozen.id, + 'price_unit': item1.list_price, + }), + (0, 0, { + 'name': item1.name, + 'product_id': item1.id, + 'product_uom_qty': 1, + 'product_uom': uom_dozen.id, + 'price_unit': item1.list_price, + }), + (0, 0, { + 'name': item1.name, + 'product_id': item1.id, + 'product_uom_qty': 1, + 'product_uom': uom_dozen.id, + 'price_unit': item1.list_price, + }), + ], + }) + so1.action_confirm() + + self.assertEqual(len(so1.picking_ids.move_lines), 3) + so1.picking_ids.write({ + 'move_line_ids': [ + (0, 0, { + 'product_id': item1.id, + 'product_uom_qty': 0, + 'qty_done': 1, + 'product_uom_id': uom_dozen.id, + 'location_id': so1.picking_ids.location_id.id, + 'location_dest_id': so1.picking_ids.location_dest_id.id, + }), + (0, 0, { + 'product_id': item1.id, + 'product_uom_qty': 0, + 'qty_done': 1, + 'product_uom_id': uom_dozen.id, + 'location_id': so1.picking_ids.location_id.id, + 'location_dest_id': so1.picking_ids.location_dest_id.id, + }), + (0, 0, { + 'product_id': item1.id, + 'product_uom_qty': 0, + 'qty_done': 1, + 'product_uom_id': uom_dozen.id, + 'location_id': so1.picking_ids.location_id.id, + 'location_dest_id': so1.picking_ids.location_dest_id.id, + }), + ], + }) + so1.picking_ids.button_validate() + self.assertEqual(so1.picking_ids.state, 'done') + self.assertEqual(so1.order_line.mapped('qty_delivered'), [1, 1, 1]) + diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index f9066831603..73a96b0b0ea 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -631,8 +631,9 @@ class Picking(models.Model): # # Link existing moves or add moves when no one is related for ops in pick.move_line_ids.filtered(lambda x: not x.move_id): # Search move with this product - moves = pick.move_lines.filtered(lambda x: x.product_id == ops.product_id) - if moves: #could search move that needs it the most (that has some quantities left) + moves = pick.move_lines.filtered(lambda x: x.product_id == ops.product_id) + moves = sorted(moves, key=lambda m: m.quantity_done < m.product_qty, reverse=True) + if moves: ops.move_id = moves[0].id else: new_move = self.env['stock.move'].create({ @@ -802,7 +803,7 @@ class Picking(models.Model): quantity_done[quant.product_id.id] += quant.qty for pack in self.mapped('move_line_ids').filtered(lambda x: x.product_id and not x.move_id): quantity_done.setdefault(pack.product_id.id, 0) - quantity_done[pack.product_id.id] += pack.qty_done + quantity_done[pack.product_id.id] += pack.product_uom_id._compute_quantity(pack.qty_done, pack.product_id.uom_id) return any(quantity_done[x] < quantity_todo.get(x, 0) for x in quantity_done) @api.multi diff --git a/odoo/addons/base/models/ir_attachment.py b/odoo/addons/base/models/ir_attachment.py index b33ce5df2a2..088a4ba3b34 100644 --- a/odoo/addons/base/models/ir_attachment.py +++ b/odoo/addons/base/models/ir_attachment.py @@ -313,6 +313,8 @@ class IrAttachment(models.Model): def _check_serving_attachments(self): # restrict writing on attachments that could be served by the # ir.http's dispatch exception handling + if self.env.user._is_superuser(): + return if self.type == 'binary' and self.url: has_group = self.env.user.has_group if not any([has_group(g) for g in self.get_serving_groups()]):