diff --git a/addons/pos_sale/models/__init__.py b/addons/pos_sale/models/__init__.py index b23ba5d574e4538c0d75a74267177ce3435c1962..5e5b400d6daeeccbb6f91c12cbaf25c1b95ece9b 100644 --- a/addons/pos_sale/models/__init__.py +++ b/addons/pos_sale/models/__init__.py @@ -6,3 +6,4 @@ from . import pos_order from . import crm_team from . import pos_session from . import sale_order +from . import stock_picking diff --git a/addons/pos_sale/models/stock_picking.py b/addons/pos_sale/models/stock_picking.py new file mode 100644 index 0000000000000000000000000000000000000000..999f1a955fd60e1b3a240a3313cff014969cfe07 --- /dev/null +++ b/addons/pos_sale/models/stock_picking.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + def _create_move_from_pos_order_lines(self, lines): + lines_to_unreserve = self.env['pos.order.line'] + for line in lines: + if line.order_id.to_ship: + continue + if any(wh != line.order_id.config_id.warehouse_id for wh in line.sale_order_line_id.move_ids.location_id.warehouse_id): + continue + lines_to_unreserve |= line + lines_to_unreserve.sale_order_line_id.move_ids.filtered(lambda ml: ml.state not in ['cancel', 'done'])._do_unreserve() + return super()._create_move_from_pos_order_lines(lines) diff --git a/addons/pos_sale/static/tests/tours/pos_sale_tours.js b/addons/pos_sale/static/tests/tours/pos_sale_tours.js index a6f3355812e3219fc1bc56bcc37fb90be79c1ee6..4957ecd4ae49e4445a6e23a3beee3144db9137a7 100644 --- a/addons/pos_sale/static/tests/tours/pos_sale_tours.js +++ b/addons/pos_sale/static/tests/tours/pos_sale_tours.js @@ -52,4 +52,17 @@ odoo.define('pos_sale.tour', function (require) { ProductScreen.check.totalAmountIs(100); Tour.register('PosSettleOrderWithPromotions', { test: true, url: '/pos/ui' }, getSteps()); + + startSteps(); + + ProductScreen.do.confirmOpeningPopup(); + ProductScreen.do.clickQuotationButton(); + ProductScreen.do.selectFirstOrder(); + ProductScreen.check.totalAmountIs(40); + ProductScreen.do.clickPayButton(); + PaymentScreen.do.clickPaymentMethod('Bank'); + PaymentScreen.do.clickValidate(); + Chrome.do.clickTicketButton(); + + Tour.register('PosSettleOrderRealTime', { test: true, url: '/pos/ui' }, getSteps()); }); diff --git a/addons/pos_sale/tests/test_pos_sale_flow.py b/addons/pos_sale/tests/test_pos_sale_flow.py index 310f2bc79122394495f728522e3079a1dae077c3..0efaf216865cdd8ff6838b5eb4557113eb01e935 100644 --- a/addons/pos_sale/tests/test_pos_sale_flow.py +++ b/addons/pos_sale/tests/test_pos_sale_flow.py @@ -166,3 +166,76 @@ class TestPoSSale(TestPointOfSaleHttpCommon): }) self.main_pos_config.open_session_cb() self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleOrderWithPromotions', login="accountman") + + def test_settle_order_unreserve_order_lines(self): + #create a product category that use the closest location for the removal strategy + self.removal_strategy = self.env['product.removal'].search([('method', '=', 'closest')], limit=1) + self.product_category = self.env['product.category'].create({ + 'name': 'Product Category', + 'removal_strategy_id': self.removal_strategy.id, + }) + + self.product = self.env['product.product'].create({ + 'name': 'Product', + 'available_in_pos': True, + 'type': 'product', + 'lst_price': 10.0, + 'taxes_id': False, + 'categ_id': self.product_category.id, + }) + + #create 2 stock location Shelf 1 and Shelf 2 + self.warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + self.shelf_1 = self.env['stock.location'].create({ + 'name': 'Shelf 1', + 'usage': 'internal', + 'location_id': self.warehouse.lot_stock_id.id, + }) + self.shelf_2 = self.env['stock.location'].create({ + 'name': 'Shelf 2', + 'usage': 'internal', + 'location_id': self.warehouse.lot_stock_id.id, + }) + + quants = self.env['stock.quant'].with_context(inventory_mode=True).create({ + 'product_id': self.product.id, + 'inventory_quantity': 2, + 'location_id': self.shelf_1.id, + }) + quants |= self.env['stock.quant'].with_context(inventory_mode=True).create({ + 'product_id': self.product.id, + 'inventory_quantity': 5, + 'location_id': self.shelf_2.id, + }) + quants.action_apply_inventory() + + sale_order = self.env['sale.order'].create({ + 'partner_id': self.env.ref('base.res_partner_2').id, + 'order_line': [(0, 0, { + 'product_id': self.product.id, + 'name': self.product.name, + 'product_uom_qty': 4, + 'price_unit': self.product.lst_price, + })], + }) + sale_order.action_confirm() + + self.assertEqual(sale_order.order_line.move_ids.move_line_ids[0].product_qty, 2) + self.assertEqual(sale_order.order_line.move_ids.move_line_ids[0].location_id.id, self.shelf_1.id) + self.assertEqual(sale_order.order_line.move_ids.move_line_ids[1].product_qty, 2) + self.assertEqual(sale_order.order_line.move_ids.move_line_ids[1].location_id.id, self.shelf_2.id) + + self.config = self.env['res.config.settings'].create({ + 'update_stock_quantities': 'real', + }) + self.config.execute() + + self.main_pos_config.open_session_cb() + self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleOrderRealTime', login="accountman") + + pos_order = self.env['pos.order'].search([], order='id desc', limit=1) + self.assertEqual(pos_order.picking_ids.move_line_ids[0].qty_done, 2) + self.assertEqual(pos_order.picking_ids.move_line_ids[0].location_id.id, self.shelf_1.id) + self.assertEqual(pos_order.picking_ids.move_line_ids[1].qty_done, 2) + self.assertEqual(pos_order.picking_ids.move_line_ids[1].location_id.id, self.shelf_2.id) + self.assertEqual(sale_order.order_line.move_ids.move_lines_count, 0)