From bdbc7a628779fee4315d0cbe409c0a7c9e9fb606 Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Tue, 14 Nov 2017 10:54:21 +0100 Subject: [PATCH] [IMP] stock: Convert yml file to python and xml --- addons/stock/__manifest__.py | 15 +- addons/stock/data/stock_data.xml | 104 ++++ addons/stock/data/stock_data.yml | 33 -- addons/stock/data/stock_demo.yml | 354 -------------- addons/stock/data/stock_demo2.xml | 449 ++++++++++++++++++ addons/stock/data/stock_demo_pre.xml | 17 + addons/stock/data/stock_demo_pre.yml | 17 - .../stock/data/stock_location_demo_cpu3.xml | 27 ++ .../stock/data/stock_location_demo_cpu3.yml | 20 - addons/stock/data/stock_orderpoint_demo.xml | 10 +- addons/stock/data/stock_orderpoint_demo.yml | 6 - addons/stock/test/packing.yml | 173 ------- addons/stock/test/packingneg.yml | 163 ------- addons/stock/test/procrule.yml | 54 --- addons/stock/test/stock_users.yml | 28 -- addons/stock/test/wiseoperator.yml | 159 ------- addons/stock/tests/__init__.py | 4 + addons/stock/tests/test_packing.py | 179 +++++++ addons/stock/tests/test_packing_neg.py | 171 +++++++ addons/stock/tests/test_proc_rule.py | 69 +++ addons/stock/tests/test_wise_operator.py | 171 +++++++ 21 files changed, 1201 insertions(+), 1022 deletions(-) delete mode 100644 addons/stock/data/stock_data.yml delete mode 100644 addons/stock/data/stock_demo.yml create mode 100644 addons/stock/data/stock_demo2.xml create mode 100644 addons/stock/data/stock_demo_pre.xml delete mode 100644 addons/stock/data/stock_demo_pre.yml create mode 100644 addons/stock/data/stock_location_demo_cpu3.xml delete mode 100644 addons/stock/data/stock_location_demo_cpu3.yml delete mode 100644 addons/stock/data/stock_orderpoint_demo.yml delete mode 100644 addons/stock/test/packing.yml delete mode 100644 addons/stock/test/packingneg.yml delete mode 100644 addons/stock/test/procrule.yml delete mode 100644 addons/stock/test/stock_users.yml delete mode 100644 addons/stock/test/wiseoperator.yml create mode 100644 addons/stock/tests/test_packing.py create mode 100644 addons/stock/tests/test_packing_neg.py create mode 100644 addons/stock/tests/test_proc_rule.py create mode 100644 addons/stock/tests/test_wise_operator.py diff --git a/addons/stock/__manifest__.py b/addons/stock/__manifest__.py index e9f83656b3f..a5c03d60e51 100644 --- a/addons/stock/__manifest__.py +++ b/addons/stock/__manifest__.py @@ -11,14 +11,13 @@ 'category': 'Warehouse', 'sequence': 13, 'demo': [ - 'data/stock_demo_pre.yml', + 'data/stock_demo_pre.xml', 'data/procurement_demo.xml', 'data/stock_demo.xml', 'data/stock_orderpoint_demo.xml', - 'data/stock_orderpoint_demo.yml', - 'data/stock_demo.yml', + 'data/stock_demo2.xml', 'data/stock_location_demo_cpu1.xml', - 'data/stock_location_demo_cpu3.yml', + 'data/stock_location_demo_cpu3.xml', 'data/stock_quant_demo.xml', ], 'data': [ @@ -66,20 +65,12 @@ 'data/default_barcode_patterns.xml', 'data/stock_data.xml', - 'data/stock_data.yml', 'data/stock_incoterms_data.xml', 'data/stock_sequence_data.xml', ], 'qweb': [ 'static/src/xml/stock_traceability_report_backend.xml', ], - 'test': [ - 'test/stock_users.yml', - 'test/packing.yml', - 'test/packingneg.yml', - 'test/procrule.yml', - 'test/wiseoperator.yml', - ], 'installable': True, 'application': True, 'auto_install': False, diff --git a/addons/stock/data/stock_data.xml b/addons/stock/data/stock_data.xml index 0e4dbe07a3c..c1eb6a36d1a 100644 --- a/addons/stock/data/stock_data.xml +++ b/addons/stock/data/stock_data.xml @@ -143,5 +143,109 @@ 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + stock.location + stock + stock_location_stock + + + stock.location + stock + stock_location_company + + + stock.location + stock + stock_location_output + + + stock.location + stock + location_pack_zone + + + stock.picking.type + stock + picking_type_internal + + + stock.picking.type + stock + picking_type_in + + + stock.picking.type + stock + picking_type_out + + + + + + diff --git a/addons/stock/data/stock_data.yml b/addons/stock/data/stock_data.yml deleted file mode 100644 index bf419b360fc..00000000000 --- a/addons/stock/data/stock_data.yml +++ /dev/null @@ -1,33 +0,0 @@ -- - !python {model: res.partner, id: base.main_partner}: | - main_warehouse = self.env['stock.warehouse'].browse(ref('warehouse0')) - self.write({'property_stock_customer':main_warehouse.lot_stock_id.id}) - -- - !python {model: ir.model.data, id: False}: | - main_warehouse = self.env['stock.warehouse'].browse(ref('warehouse0')) - - #create xml ids for demo data that are widely used in tests or in other codes, for more convenience - xml_references = [ - {'name': 'stock_location_stock', 'module': 'stock', 'model': 'stock.location', 'res_id': main_warehouse.lot_stock_id.id}, - {'name': 'stock_location_company', 'module': 'stock', 'model': 'stock.location', 'res_id': main_warehouse.wh_input_stock_loc_id.id}, - {'name':'stock_location_output','module':'stock', 'model':'stock.location','res_id':main_warehouse.wh_output_stock_loc_id.id}, - {'name':'location_pack_zone','module':'stock', 'model':'stock.location','res_id':main_warehouse.wh_pack_stock_loc_id.id}, - {'name':'picking_type_internal','module':'stock', 'model':'stock.picking.type','res_id':main_warehouse.int_type_id.id}, - {'name':'picking_type_in','module':'stock', 'model':'stock.picking.type','res_id':main_warehouse.in_type_id.id}, - {'name':'picking_type_out','module':'stock', 'model':'stock.picking.type','res_id':main_warehouse.out_type_id.id}, - ] - for xml_record in xml_references: - xml_ids = self.search([('module', '=', xml_record['module']), ('model', '=', xml_record['model']), ('name', '=', xml_record['name'])]).ids - if not xml_ids: - self.create(xml_record) - #avoid the xml id and the associated resource being dropped by the orm by manually making a hit on it - self._update_dummy(xml_record['model'], xml_record['module'], xml_record['name']) - -- - !python {model: res.company, id: False}: | - #create the transit location for each company existing - companies = self.search([('internal_transit_location_id', '=', False)]) - for company in companies: - company.create_transit_location() - diff --git a/addons/stock/data/stock_demo.yml b/addons/stock/data/stock_demo.yml deleted file mode 100644 index 697bd8a2cad..00000000000 --- a/addons/stock/data/stock_demo.yml +++ /dev/null @@ -1,354 +0,0 @@ -- - !python {model: ir.model.data, id: False}: | - chicago_warehouse = self.env['stock.warehouse'].browse(ref('stock_warehouse_shop0')) - - #create xml ids for demo data that are widely used in tests or in other codes, for more convenience - xml_references = [ - {'name':'chi_picking_type_in','module':'stock', 'model':'stock.picking.type','res_id': chicago_warehouse.in_type_id.id}, - {'name':'chi_picking_type_out','module':'stock', 'model':'stock.picking.type','res_id': chicago_warehouse.out_type_id.id}, - {'name':'stock_location_shop0','module':'stock', 'model':'stock.location','res_id': chicago_warehouse.lot_stock_id.id}, - ] - for xml_record in xml_references: - xml_ids = self.search([('module', '=', xml_record['module']), ('model', '=', xml_record['model']), ('name', '=', xml_record['name'])]).ids - if not xml_ids: - self.create(xml_record) - #avoid the xml id and the associated resource being dropped by the orm by manually making a hit on it - self._update_dummy(xml_record['model'], xml_record['module'], xml_record['name']) -- - !record {model: stock.location, id: location_refrigerator_small}: - name: Small Refrigerator - usage: internal - location_id: stock_location_14 -- - !record {model: product.product, id: product_icecream}: - default_code: 001 - name: Ice Cream - type: product - categ_id: product.product_category_1 - lst_price: 100.0 - standard_price: 70.0 - weight: 1.0 - uom_id: product.product_uom_kgm - property_stock_inventory: location_inventory - description: Ice cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events. -- - !record {model: stock.production.lot, id: lot_icecream_0}: - name: '0000000000017' - product_id: product_icecream -- - !record {model: stock.production.lot, id: lot_icecream_1}: - name: '0000000000024' - product_id: product_icecream -- - !record {model: stock.inventory, id: stock_inventory_icecream}: - name: Inventory for icecream - filter: 'product' - product_id: product_icecream -- - !record {model: stock.inventory.line, id: stock_inventory_line_icecream_lot0}: - product_id: product_icecream - product_uom_id: product.product_uom_kgm - inventory_id: stock_inventory_icecream - product_qty: 50.0 - prod_lot_id: lot_icecream_0 - location_id: stock_location_14 -- - !record {model: stock.inventory.line, id: stock_inventory_line_icecream_lot1}: - product_id: product_icecream - product_uom_id: product.product_uom_kgm - inventory_id: stock_inventory_icecream - product_qty: 40.0 - prod_lot_id: lot_icecream_1 - location_id: stock_location_14 -- - Create STOCK_MOVE for OUT -- - !record {model: stock.picking, id: outgoing_shipment_main_warehouse}: - picking_type_id: stock.picking_type_out - origin: 'outgoing shipment main_warehouse' - partner_id: base.res_partner_1 - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers - move_lines: - - product_id: product.product_product_27 - product_uom: product.product_uom_unit - product_uom_qty: 15.0 - picking_type_id: stock.picking_type_out - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers -- - !record {model: stock.picking, id: outgoing_shipment_main_warehouse1}: - picking_type_id: stock.picking_type_out - origin: 'outgoing shipment' - partner_id: base.res_partner_1 - date: !eval "'%s-%s-15' %((datetime.now()-timedelta(days=60)).year, (datetime.now()-timedelta(days=60)).month)" - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers - move_lines: - - product_id: product.product_product_6 - product_uom: product.product_uom_unit - product_uom_qty: 180.0 - picking_type_id: stock.picking_type_out - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers - move_line_ids: - - product_id: product.product_product_6 - qty_done: 100 - product_uom_id: product.product_uom_unit - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers -- - !record {model: stock.picking, id: outgoing_shipment_main_warehouse2}: - picking_type_id: stock.picking_type_out - origin: 'outgoing shipment your_company warehouse' - partner_id: base.res_partner_1 - date: !eval "'%s-%s-15' % ((datetime.now()-timedelta(days=30)).month,(datetime.now()-timedelta(days=30)).month)" - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers - move_lines: - - product_id: product.product_delivery_02 - product_uom: product.product_uom_unit - product_uom_qty: 45.0 - picking_type_id: stock.picking_type_out - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers -- - !record {model: stock.picking, id: outgoing_shipment_main_warehouse3}: - picking_type_id: stock.picking_type_out - origin: 'your company warehouse' - partner_id: base.res_partner_1 - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers - move_lines: - - product_id: product.product_product_5 - product_uom: product.product_uom_unit - product_uom_qty: 75.0 - picking_type_id: stock.picking_type_out - location_id: stock.stock_location_stock - location_dest_id: stock.stock_location_customers -- - Create STOCK_PICKING for IN -- - !record {model: stock.picking, id: incomming_shipment}: - picking_type_id: stock.picking_type_in - location_id: stock_location_suppliers - location_dest_id: stock_location_stock -- - !record {model: stock.move, id: incomming_shipment_icecream}: - product_id: product_icecream - product_uom: product.product_uom_kgm - product_uom_qty: 50.0 - picking_type_id: stock.picking_type_in - location_id: stock_location_suppliers - location_dest_id: stock_location_stock - picking_id: incomming_shipment -- - !record {model: stock.picking, id: incomming_shipment1}: - picking_type_id: stock.picking_type_in - partner_id: base.res_partner_1 - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock - move_lines: - - product_id: product.product_delivery_01 - product_uom: product.product_uom_unit - product_uom_qty: 35.0 - picking_type_id: stock.picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock -- - !record {model: stock.picking, id: incomming_shipment2}: - picking_type_id: stock.picking_type_in - partner_id: base.res_partner_1 - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock - move_lines: - - product_id: product.product_product_22 - product_uom: product.product_uom_unit - product_uom_qty: 125.0 - picking_type_id: stock.picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock -- - !record {model: stock.picking, id: incomming_shipment3}: - picking_type_id: stock.picking_type_in - partner_id: base.res_partner_1 - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock - move_lines: - - product_id: product.product_product_10 - product_uom: product.product_uom_unit - product_uom_qty: 120.0 - picking_type_id: stock.picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock -- - !record {model: stock.picking, id: incomming_shipment4}: - picking_type_id: stock.picking_type_in - partner_id: base.res_partner_1 - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock - move_lines: - - product_id: product.product_delivery_02 - product_uom: product.product_uom_unit - product_uom_qty: 50.0 - picking_type_id: stock.picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock -- - Create STOCK_PICKING_IN for Chicago Warehouse -- - !record {model: stock.picking, id: incomming_chicago_warehouse}: - picking_type_id: chi_picking_type_in - origin: 'incoming_chicago_warehouse' - partner_id: base.res_partner_1 - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 - move_lines: - - product_id: product.product_product_9 - product_uom: product.product_uom_unit - product_uom_qty: 25.0 - picking_type_id: chi_picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 - -- - !record {model: stock.picking, id: incomming_chicago_warehouse1}: - picking_type_id: chi_picking_type_in - partner_id: base.res_partner_1 - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 - move_lines: - - product_id: product.product_delivery_01 - product_uom: product.product_uom_unit - product_uom_qty: 180.0 - picking_type_id: chi_picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 - state: done - move_line_ids: - - product_id: product.product_delivery_01 - qty_done: 100 - product_uom_id: product.product_uom_unit - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 -- - !record {model: stock.picking, id: incomming_chicago_warehouse2}: - picking_type_id: chi_picking_type_in - partner_id: base.res_partner_1 - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 - move_lines: - - product_id: product.product_delivery_01 - product_uom: product.product_uom_unit - product_uom_qty: 45.0 - picking_type_id: chi_picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 -- - !record {model: stock.picking, id: incomming_chicago_warehouse3}: - picking_type_id: chi_picking_type_in - origin: 'chicago_warehouse' - partner_id: base.res_partner_1 - date: !eval "'%s-%s-2' % ((datetime.now()-timedelta(days=30)).month,(datetime.now()-timedelta(days=30)).month)" - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 - move_lines: - - product_id: product.product_product_22 - product_uom: product.product_uom_unit - product_uom_qty: 75.0 - picking_type_id: chi_picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_shop0 -- - Create STOCK_PICKING_OUT for Chicago Warehouse -- - !record {model: stock.picking, id: outgoing_chicago_warehouse}: - picking_type_id: chi_picking_type_out - origin: 'outgoing_chicago_warehouse' - partner_id: base.res_partner_1 - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers - move_lines: - - product_id: product.product_product_27 - product_uom: product.product_uom_unit - product_uom_qty: 15.0 - picking_type_id: chi_picking_type_out - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers -- - !record {model: stock.picking, id: outgoing_chicago_warehouse1}: - picking_type_id: chi_picking_type_out - origin: 'outgoing_shipment_chicago_warehouse' - partner_id: base.res_partner_1 - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers - move_lines: - - product_id: product.product_product_6 - product_uom: product.product_uom_unit - product_uom_qty: 180.0 - picking_type_id: chi_picking_type_out - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers - move_line_ids: - - product_id: product.product_product_6 - qty_done: 100 - product_uom_id: product.product_uom_unit - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers -- - !record {model: stock.picking, id: outgoing_chicago_warehouse2}: - picking_type_id: chi_picking_type_out - origin: 'chicago_warehouse' - partner_id: base.res_partner_1 - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers - move_lines: - - product_id: product.product_delivery_02 - product_uom: product.product_uom_unit - product_uom_qty: 45.0 - picking_type_id: chi_picking_type_out - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers -- - !record {model: stock.picking, id: outgoing_chicago_warehouse3}: - picking_type_id: chi_picking_type_out - origin: 'outgoing chicago warehouse' - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers - partner_id: base.res_partner_1 - move_lines: - - product_id: product.product_product_5 - product_uom: product.product_uom_unit - product_uom_qty: 75.0 - picking_type_id: chi_picking_type_out - location_id: stock.stock_location_shop0 - location_dest_id: stock.stock_location_customers -- - !python {model: stock.picking, id: False}: | - self.browse([ - ref('outgoing_shipment_main_warehouse'), - ref('outgoing_shipment_main_warehouse1'), - ref('incomming_shipment2'), - ref('incomming_shipment1'), - ref('incomming_shipment3'), - ref('outgoing_chicago_warehouse3'), - ref('outgoing_chicago_warehouse2'), - ref('outgoing_chicago_warehouse1'), - ref('incomming_chicago_warehouse1'), - ref('incomming_chicago_warehouse2'), - ref('incomming_chicago_warehouse3') - ]).action_confirm() - record = self.browse(ref('outgoing_shipment_main_warehouse1')) - record_chi_in = self.browse(ref('incomming_chicago_warehouse1')) - record_chi_out = self.browse(ref('outgoing_chicago_warehouse1')) - if record.state != 'done' and record_chi_in.state != 'done' and record_chi_out.state != 'done': - self.browse([ - ref('outgoing_shipment_main_warehouse'), - ref('outgoing_chicago_warehouse3'), - ref('outgoing_shipment_main_warehouse2') - ]).force_assign() - self.browse([ - ref('outgoing_shipment_main_warehouse1'), - ref('incomming_chicago_warehouse1'), - ref('outgoing_chicago_warehouse1') - ]).do_transfer() diff --git a/addons/stock/data/stock_demo2.xml b/addons/stock/data/stock_demo2.xml new file mode 100644 index 00000000000..6cfe2865c3d --- /dev/null +++ b/addons/stock/data/stock_demo2.xml @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + stock.picking.type + stock + chi_picking_type_in + + + stock.picking.type + stock + chi_picking_type_out + + + stock.picking.type + stock + stock_location_shop0 + + + + Small Refrigerator + internal + + + + + 001 + Ice Cream + product + + 100.0 + 70.0 + 1.0 + + + + + Ice cream can be mass-produced and thus is widely available in developed parts + of the world. Ice cream can be purchased in large cartons (vats and squrounds) + from supermarkets and grocery stores, in smaller quantities from ice cream shops, + convenience stores, and milk bars, and in individual servings from small carts or + vans at public events. + + + + + 0000000000017 + + + + + 0000000000024 + + + + + Inventory for icecream + product + + + + + + + + 50.0 + + + + + + + + + 40.0 + + + + + + + + outgoing shipment main_warehouse + + + + + + + + + outgoing shipment + + + + + + + + + + outgoing shipment your_company warehouse + + + + + + + + + + your company warehouse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + incoming_chicago_warehouse + + + + + + + + + + + + + + + + + + + + + + + + + chicago_warehouse + + + + + + + + + + + outgoing_chicago_warehouse + + + + + + + + + + outgoing_shipment_chicago_warehouse + + + + + + + + + + chicago_warehouse + + + + + + + + + + outgoing chicago warehouse + + + + + + + + + + + + + + + + + + + + diff --git a/addons/stock/data/stock_demo_pre.xml b/addons/stock/data/stock_demo_pre.xml new file mode 100644 index 00000000000..f72e84cdfc2 --- /dev/null +++ b/addons/stock/data/stock_demo_pre.xml @@ -0,0 +1,17 @@ + + + + + Shelf 2 + 0 + + + + Shelf 1 + 0 + + + + diff --git a/addons/stock/data/stock_demo_pre.yml b/addons/stock/data/stock_demo_pre.yml deleted file mode 100644 index bcec8635444..00000000000 --- a/addons/stock/data/stock_demo_pre.yml +++ /dev/null @@ -1,17 +0,0 @@ -- - !record {model: stock.location, id: stock_location_14}: - name: Shelf 2 - posx: 0 - -- - !record {model: stock.location, id: stock_location_components}: - name: Shelf 1 - posx: 0 - -- - !python {model: ir.model.data, id: False}: | - main_warehouse = self.env['stock.warehouse'].browse(ref('warehouse0')) - self.env['stock.location'].browse([ - ref('stock_location_14'), - ref('stock_location_components') - ]).write({'location_id':main_warehouse.lot_stock_id.id}) diff --git a/addons/stock/data/stock_location_demo_cpu3.xml b/addons/stock/data/stock_location_demo_cpu3.xml new file mode 100644 index 00000000000..08d4cf55dca --- /dev/null +++ b/addons/stock/data/stock_location_demo_cpu3.xml @@ -0,0 +1,27 @@ + + + + Order Processing + internal + + + + + Dispatch Zone + internal + + + + + Gate A + internal + + + + + Gate B + internal + + + + diff --git a/addons/stock/data/stock_location_demo_cpu3.yml b/addons/stock/data/stock_location_demo_cpu3.yml deleted file mode 100644 index c4323d65f9d..00000000000 --- a/addons/stock/data/stock_location_demo_cpu3.yml +++ /dev/null @@ -1,20 +0,0 @@ -- - !record {model: stock.location, id: location_order}: - name: Order Processing - usage: internal - location_id: stock.stock_location_company -- - !record {model: stock.location, id: location_dispatch_zone}: - name: Dispatch Zone - usage: internal - location_id: location_order -- - !record {model: stock.location, id: location_gate_a}: - name: Gate A - usage: internal - location_id: location_dispatch_zone -- - !record {model: stock.location, id: location_gate_b}: - name: Gate B - usage: internal - location_id: location_dispatch_zone diff --git a/addons/stock/data/stock_orderpoint_demo.xml b/addons/stock/data/stock_orderpoint_demo.xml index 6bc757525a4..31504f3c9c0 100644 --- a/addons/stock/data/stock_orderpoint_demo.xml +++ b/addons/stock/data/stock_orderpoint_demo.xml @@ -9,7 +9,8 @@ - + 12.0 @@ -17,7 +18,8 @@ - + 5.0 @@ -25,7 +27,9 @@ - + + \ No newline at end of file diff --git a/addons/stock/data/stock_orderpoint_demo.yml b/addons/stock/data/stock_orderpoint_demo.yml deleted file mode 100644 index 7cd42b4fe8f..00000000000 --- a/addons/stock/data/stock_orderpoint_demo.yml +++ /dev/null @@ -1,6 +0,0 @@ -- - !python {model: stock.location, id: False}: | - main_warehouse = self.env['stock.warehouse'].browse(ref('warehouse0')) - main_stock = main_warehouse.lot_stock_id.id - stowar_ops = self.env['stock.warehouse.orderpoint'].search([]) - stowar_ops.write({'location_id':main_stock}) diff --git a/addons/stock/test/packing.yml b/addons/stock/test/packing.yml deleted file mode 100644 index 1ae21fe611f..00000000000 --- a/addons/stock/test/packing.yml +++ /dev/null @@ -1,173 +0,0 @@ -- - Create a new stockable product -- - !record {model: product.product, id: product1, view: False}: - name: Nice product - type: product - categ_id: product.product_category_1 - list_price: 100.0 - standard_price: 70.0 - seller_ids: - - delay: 1 - name: base.res_partner_2 - min_qty: 2.0 - uom_id: product.product_uom_unit - uom_po_id: product.product_uom_unit -- - Create an incoming picking for this product of 300 PCE from suppliers to stock -- - !record {model: stock.picking, id: pick1}: - name: Incoming picking - partner_id: base.res_partner_2 - picking_type_id: picking_type_in - move_lines: - - product_id: product1 - product_uom_qty: 300.00 - location_id: stock_location_suppliers - location_dest_id: stock_location_stock -- - Confirm and assign picking -- - !python {model: stock.picking, id: pick1}: | - self.action_confirm() - self.action_assign() -- - Put 120 pieces on Pallet 1 (package), 120 pieces on Pallet 2 with lot A and 60 pieces on Pallet 3 -- - !python {model: stock.picking, id: pick1}: | - #Change quantity of first to 120 and create 2 others quant operations - stock_pack = self.env['stock.move.line'] - stock_quant_pack = self.env['stock.quant.package'] - #create lot A - lot_a = self.env['stock.production.lot'].create({'name': 'Lot A', 'product_id': ref('product1')}) - #create package - package1 = stock_quant_pack.create({'name': 'Pallet 1'}) - package2 = stock_quant_pack.create({'name': 'Pallet 2'}) - package3 = stock_quant_pack.create({'name': 'Pallet 3'}) - #Create package for each line and assign it as result_package_id - #create pack operation - self.move_line_ids[0].write({'result_package_id': package1.id, 'qty_done': 120}) - new_pack1 = stock_pack.create({ - 'product_id': ref('product1'), - 'product_uom_id': ref('product.product_uom_unit'), - 'picking_id': ref('pick1'), - 'lot_id': lot_a.id, - 'qty_done': 120, - 'result_package_id': package2.id, - 'location_id': ref('stock_location_suppliers'), - 'location_dest_id': ref('stock_location_stock') - }) - new_pack2 = stock_pack.create({ - 'product_id': ref('product1'), - 'product_uom_id': ref('product.product_uom_unit'), - 'picking_id': ref('pick1'), - 'result_package_id': package3.id, - 'qty_done': 60, - 'location_id': ref('stock_location_suppliers'), - 'location_dest_id': ref('stock_location_stock') - }) -- - Transfer the receipt -- - !python {model: stock.picking, id: pick1}: | - self.action_done() -- - Check the system created 5 quants one with 120 pieces on pallet 1, one with 120 pieces on pallet 2 with lot A and 60 pieces on pallet 3 in stock - and 1 with -180 pieces and another with -120 piece lot A in supplier -- - !python {model: stock.quant, id: False}: | - quants = self.search([('product_id','=',ref('product1'))]) - assert len(quants.ids) == 5, "The number of quants created is not correct" - for quant in quants: - if quant.package_id.name == 'Pallet 1': - assert quant.quantity == 120, "Should have 120 pieces on pallet 1" - elif quant.package_id.name == 'Pallet 2': - assert quant.quantity == 120, "Should have 120 pieces on pallet 2" - elif quant.package_id.name == 'Pallet 3': - assert quant.quantity == 60, "Should have 60 pieces on pallet 3" -- - Check there is no backorder or extra moves created -- - !python {model: stock.picking, id: pick1}: | - backorder = self.search([('backorder_id', '=', ref('pick1'))]) - assert not backorder, "" - #Check extra moves created - assert len(self.move_lines) == 1, "" -- - Make a delivery order of 300 pieces to the customer -- - !record {model: stock.picking, id: delivery_order1}: - name: outgoing picking - partner_id: base.res_partner_4 - picking_type_id: stock.picking_type_out - move_lines: - - product_id: product1 - product_uom_qty: 300.00 - location_id: stock_location_stock - location_dest_id: stock_location_customers -- - Assign and confirm -- - !python {model: stock.picking, id: delivery_order1}: | - self.action_confirm() - self.action_assign() -- - Instead of doing the 300 pieces, you decide to take pallet 1 (do not mention product in operation here) and 20 pieces from lot A and 10 pieces from pallet 3 -- - !python {model: stock.picking, id: delivery_order1}: | - for rec in self.move_line_ids: - if rec.package_id.name == 'Pallet 1': - rec.qty_done = 120 - rec.result_package_id = False - if rec.package_id.name == 'Pallet 2': - rec.qty_done = 20 - rec.result_package_id = False - if rec.package_id.name == 'Pallet 3': - rec.qty_done = 10 - rec.result_package_id = False -- - Process this picking -- - !python {model: stock.picking, id: delivery_order1 }: | - self.action_done() -- - Check the quants that you have 100 pieces of pallet 2 and 50 pieces of pallet 3 in stock -- - !python {model: stock.quant, id: False}: | - records = self.search([('product_id','=',ref('product1'))]) - for rec in records: - if rec.package_id.name == 'Pallet 2' and rec.location_id.id == ref('stock_location_stock'): - assert rec.quantity == 100, "Should have 100 pieces in stock on pallet 2, got " + str(rec.quantity) - elif rec.package_id.name == 'Pallet 3' and rec.location_id.id == ref('stock_location_stock'): - assert rec.quantity == 50, "Should have 50 pieces in stock on pallet 3" - else: - assert rec.location_id.id != ref('stock_location_stock'), "Unrecognized quant in stock" -- - Check a backorder was created and on that backorder, prepare partial and process backorder -- - !python {model: stock.picking, id: False}: | - backorders = self.search([('backorder_id', '=', ref('delivery_order1'))]) - assert backorders, "Backorder should have been created" - backorders.action_assign() - picking = backorders[0] - assert len(picking.move_line_ids) == 2, "Wrong number of pack operation" - for pack_op in picking.move_line_ids: - assert pack_op.package_id.name in ('Pallet 2', 'Pallet 3'), "Wrong pallet info in pack operation (%s found)" % (pack_op.package_id.name) - if pack_op.package_id.name == 'Pallet 2': - assert pack_op.product_qty == 100 - pack_op.qty_done = 100 - elif pack_op.package_id.name == 'Pallet 3': - assert pack_op.product_qty == 50 - pack_op.qty_done = 50 - backorders.action_done() -- - Check there are still 0 pieces in stock -- - !python {model: stock.quant, id: False}: | - records = self.search([('product_id','=',ref('product1')), ('location_id', '=', ref('stock_location_stock'))]) - total_qty = 0 - for rec in records: - total_qty += rec.quantity - product = self.env["product.product"].browse(ref('product1')) - assert total_qty == 0, "Total quantity in stock should be 0 as the backorder took everything out of stock" - assert product.qty_available == 0, "Quantity available should be 0 too" diff --git a/addons/stock/test/packingneg.yml b/addons/stock/test/packingneg.yml deleted file mode 100644 index 4c4ca1d1cbc..00000000000 --- a/addons/stock/test/packingneg.yml +++ /dev/null @@ -1,163 +0,0 @@ -- - Create a new "negative" stockable product -- - !record {model: product.product, id: product_neg, view: False}: - name: Negative product - type: product - categ_id: product.product_category_1 - list_price: 100.0 - standard_price: 70.0 - seller_ids: - - delay: 1 - name: base.res_partner_2 - min_qty: 2.0 - uom_id: product.product_uom_unit - uom_po_id: product.product_uom_unit -- - Create an incoming picking for this product of 300 PCE from suppliers to stock -- - !record {model: stock.picking, id: pick_neg}: - name: Incoming picking (negative product) - partner_id: base.res_partner_2 - picking_type_id: picking_type_in - move_lines: - - product_id: product_neg - product_uom_qty: 300.00 - location_id: stock_location_suppliers - location_dest_id: stock_location_stock -- - Confirm and assign picking -- - !python {model: stock.picking, id: pick_neg}: | - self.action_confirm() - self.action_assign() -- - Put 120 pieces on Palneg 1 (package), 120 pieces on Palneg 2 with lot A and 60 pieces on Palneg 3 -- - !python {model: stock.picking, id: pick_neg}: | - #Change quantity of first to 120 and create 2 others quant operations - stock_pack = self.env['stock.move.line'] - stock_quant_pack = self.env['stock.quant.package'] - #create lot A - lot_a = self.env['stock.production.lot'].create({'name': 'Lot neg', 'product_id': ref('product_neg')}) - #create package - package1 = stock_quant_pack.create({'name': 'Palneg 1'}) - package2 = stock_quant_pack.create({'name': 'Palneg 2'}) - package3 = stock_quant_pack.create({'name': 'Palneg 3'}) - #Create package for each line and assign it as result_package_id - #create pack operation - self.move_line_ids[0].write({'result_package_id': package1.id, 'qty_done': 120}) - new_pack1 = stock_pack.create({ - 'product_id': ref('product_neg'), - 'product_uom_id': ref('product.product_uom_unit'), - 'picking_id': ref('pick_neg'), - 'lot_id': lot_a.id, - 'qty_done': 120, - 'result_package_id': package2.id, - 'location_id': ref('stock_location_suppliers'), - 'location_dest_id': ref('stock_location_stock') - }) - new_pack2 = stock_pack.create({ - 'product_id': ref('product_neg'), - 'product_uom_id': ref('product.product_uom_unit'), - 'picking_id': ref('pick_neg'), - 'result_package_id': package3.id, - 'qty_done': 60, - 'location_id': ref('stock_location_suppliers'), - 'location_dest_id': ref('stock_location_stock') - }) -- - Transfer the receipt -- - !python {model: stock.picking, id: pick_neg}: | - self.do_transfer() -- - Make a delivery order of 300 pieces to the customer -- - !record {model: stock.picking, id: delivery_order_neg}: - name: outgoing picking (negative product) - partner_id: base.res_partner_4 - picking_type_id: stock.picking_type_out - move_lines: - - product_id: product_neg - product_uom_qty: 300.00 - location_id: stock_location_stock - location_dest_id: stock_location_customers -- - Assign and confirm -- - !python {model: stock.picking, id: delivery_order_neg}: | - self.action_confirm() - self.action_assign() -- - Instead of doing the 300 pieces, you decide to take pallet 1 (do not mention product in operation here) and 140 pieces from lot A/pallet 2 and 10 pieces from pallet 3 -- - !python {model: stock.picking, id: delivery_order_neg}: | - for rec in self.move_line_ids: - if rec.package_id.name == 'Palneg 1': - rec.qty_done = rec.product_qty - rec.result_package_id = False - elif rec.package_id.name == 'Palneg 2' and rec.lot_id.name == 'Lot neg' : - rec.write({ - 'qty_done': 140, - 'result_package_id': False, - }) - elif rec.package_id.name == 'Palneg 3': - rec.qty_done = 10 - rec.result_package_id = False -- - Process this picking -- - !python {model: stock.picking, id: delivery_order_neg}: | - self.do_transfer() -- - Check the quants that you have -20 pieces pallet 2 in stock, and a total quantity of 50 in stock from pallet 3 (should be 20+30, as it has been split by reservation) -- - !python {model: stock.quant, id: False}: | - records = self.search([('product_id','=',ref('product_neg'))]) - pallet_3_stock_qty = 0 - for rec in records: - if rec.package_id.name == 'Palneg 2' and rec.location_id.id == ref('stock_location_stock'): - assert rec.quantity == -20, "Should have -20 pieces in stock on pallet 2. Got " + str(rec.quantity) - assert rec.lot_id.name == 'Lot neg', "It should have kept its Lot" - elif rec.package_id.name == 'Palneg 3' and rec.location_id.id == ref('stock_location_stock'): - pallet_3_stock_qty += rec.quantity - else: - assert rec.location_id.id != ref('stock_location_stock'), "Unrecognized quant in stock" - assert pallet_3_stock_qty == 50, "Should have 50 pieces in stock on pallet 3" -- - Create a picking for reconciling the negative quant -- - !record {model: stock.picking, id: delivery_reconcile}: - name: reconciling_delivery - partner_id: base.res_partner_4 - picking_type_id: stock.picking_type_in - move_lines: - - product_id: product_neg - product_uom_qty: 20.0 - location_id: stock_location_suppliers - location_dest_id: stock_location_stock -- - Receive 20 products with lot neg in stock with a new incoming shipment that should be on pallet 2 -- - !python {model: stock.picking, id: delivery_reconcile}: | - self.action_confirm() - pack_obj = self.env["stock.quant.package"] - lot = self.env["stock.production.lot"].search([('product_id', '=', ref('product_neg')), ('name','=','Lot neg')], limit=1) - pack = pack_obj.search([('name', '=', 'Palneg 2')], limit=1) - self.move_line_ids[0].write({'lot_id': lot.id, 'qty_done': 20.0, 'result_package_id': pack.id}) - self.do_transfer() -- - Check the negative quant was reconciled -- - !python {model: stock.quant, id: False}: | - neg_quants = self.search([('product_id','=', ref('product_neg')), ('quantity', '<', 0), ('location_id.id', '!=', - ref('stock_location_suppliers'))]) - assert len(neg_quants) == 0, "Negative quants should have been reconciled" - pick = self.env['stock.picking'].browse(ref('delivery_reconcile')) - customer_quant = self.search([ - ('product_id', '=', ref('product_neg')), - ('location_id', '=', ref('stock_location_customers')), - ('lot_id.name','=', 'Lot neg'), - ('quantity','=', 20) - ]) diff --git a/addons/stock/test/procrule.yml b/addons/stock/test/procrule.yml deleted file mode 100644 index d2cf3b29c79..00000000000 --- a/addons/stock/test/procrule.yml +++ /dev/null @@ -1,54 +0,0 @@ -- - Create a product route containing a procurement rule that will - generate a move from Stock for every procurement created in Output -- - !record {model: stock.location.route, id: product_route}: - name: Stock -> output route - product_selectable: True - pull_ids: - - name: Stock -> output rule - action: move - picking_type_id: stock.picking_type_internal - location_src_id: stock.stock_location_stock - location_id: stock.stock_location_output -- - Set this route on `product.product_product_3` -- - !python {model: product.product, id: product.product_product_3}: | - self.write({'route_ids': [(4, ref('stock.product_route'))]}) -- - Create Delivery Order of 10 `product.product_product_3` - from Output -> Customer -- - !record {model: stock.picking, id: pick_output}: - name: Delivery order for procurement - partner_id: base.res_partner_2 - picking_type_id: stock.picking_type_out - location_id: stock.stock_location_output - location_dest_id: stock.stock_location_customers - move_lines: - - product_id: product.product_product_3 - product_uom_qty: 10.00 - procure_method: make_to_order -- - Confirm delivery order. -- - !python {model: stock.picking, id: pick_output}: | - self.action_confirm() -- - I run the scheduler. -- - !python {model: procurement.group, id: False}: | - self.run_scheduler() -- - Check that a picking was created from stock to output. -- - !python {model: stock.move, id: False}: | - picking = self.env["stock.picking"].browse(ref("pick_output")) - moves = self.search([ - ('product_id', '=', ref('product.product_product_3')), - ('location_id', '=', ref('stock.stock_location_stock')), - ('location_dest_id', '=', ref('stock.stock_location_output')), - ('move_dest_ids', 'in', [picking.move_lines[0].id]) - ]) - assert len(moves.ids) == 1, "It should have created a picking from Stock to Output with the original picking as destination" diff --git a/addons/stock/test/stock_users.yml b/addons/stock/test/stock_users.yml deleted file mode 100644 index 6bd8f3ebbd5..00000000000 --- a/addons/stock/test/stock_users.yml +++ /dev/null @@ -1,28 +0,0 @@ -- - Create a user as 'Stock Manager' -- - !record {model: res.users, id: res_users_stock_manager, view: False}: - company_id: base.main_company - name: Stock Manager - login: sam - email: stockmanager@yourcompany.com -- - I added groups for Stock Manager. -- - !record {model: res.users, id: res_users_stock_manager}: - groups_id: - - stock.group_stock_manager -- - Create a user as 'Stock User' -- - !record {model: res.users, id: res_users_stock_user, view: False}: - company_id: base.main_company - name: Stock User - login: sau - email: stockuser@yourcompany.com -- - I added groups for Stock User. -- - !record {model: res.users, id: res_users_stock_user}: - groups_id: - - stock.group_stock_user diff --git a/addons/stock/test/wiseoperator.yml b/addons/stock/test/wiseoperator.yml deleted file mode 100644 index 07dc1520dc4..00000000000 --- a/addons/stock/test/wiseoperator.yml +++ /dev/null @@ -1,159 +0,0 @@ -- - Create a new stockable product -- - !record {model: product.product, id: product_wise}: - name: Wise Unit - type: product - categ_id: product.product_category_1 - uom_id: product.product_uom_unit - uom_po_id: product.product_uom_unit -- - Create an incoming picking for this product of 10 PCE from suppliers to stock -- - !record {model: stock.picking, id: pick1_wise}: - name: Incoming picking (wise unit) - partner_id: base.res_partner_2 - picking_type_id: picking_type_in - move_lines: - - product_id: product_wise - product_uom_qty: 10.00 - location_id: stock_location_suppliers - location_dest_id: stock_location_stock -- - Confirm and assign picking and prepare partial -- - !python {model: stock.picking, id: pick1_wise}: | - self.action_confirm() - self.action_assign() -- - Put 4 pieces in shelf1 and 6 pieces in shelf2 -- - !python {model: stock.picking, id: pick1_wise}: | - stock_pack = self.env['stock.move.line'] - stock_quant_pack = self.env['stock.quant.package'] - package1 = stock_quant_pack.create({'name': 'Pack 1'}) - self.move_line_ids[0].write({ - 'result_package_id': package1.id, - 'qty_done': 4, - 'location_dest_id': ref('stock_location_components') - }) - new_pack1 = stock_pack.create({ - 'product_id': ref('product_wise'), - 'product_uom_id': ref('product.product_uom_unit'), - 'picking_id': ref('pick1_wise'), - 'qty_done': 6.0, - 'location_id': ref('stock_location_suppliers'), - 'location_dest_id': ref('stock_location_14') - }) -- - Transfer the receipt -- - !python {model: stock.picking, id: pick1_wise}: | - self.do_transfer() -- - Check the system created 3 quants -- - !python {model: stock.quant, id: False}: | - records = self.search([('product_id','=',ref('product_wise'))]) - assert len(records.ids) == 3, "The number of quants created is not correct" -- - Make a delivery order of 5 pieces to the customer -- - !record {model: stock.picking, id: delivery_order_wise1}: - name: outgoing picking 1 (wise unit) - partner_id: base.res_partner_4 - picking_type_id: stock.picking_type_out - move_lines: - - product_id: product_wise - product_uom_qty: 5.0 - location_id: stock_location_stock - location_dest_id: stock_location_customers -- - Assign and confirm -- - !python {model: stock.picking, id: delivery_order_wise1}: | - self.action_confirm() - self.action_assign() - assert self.state == 'assigned' -- - Make a delivery order of 5 pieces to the customer -- - !record {model: stock.picking, id: delivery_order_wise2}: - name: outgoing picking 2 (wise unit) - partner_id: base.res_partner_4 - picking_type_id: stock.picking_type_out - move_lines: - - product_id: product_wise - product_uom_qty: 5.0 - location_id: stock_location_stock - location_dest_id: stock_location_customers -- - Assign and confirm -- - !python {model: stock.picking, id: delivery_order_wise2}: | - self.action_confirm() - self.action_assign() - assert self.state == 'assigned' -- - The operator is a wise guy and decides to do the opposite of what Odoo proposes. He uses the products reserved on picking 1 on picking 2 and vice versa -- - !python {model: stock.picking, id: False}: | - picking1 = self.browse(ref('delivery_order_wise1')) - picking2 = self.browse(ref('delivery_order_wise2')) - move1 = picking1.move_lines[0] - move2 = picking2.move_lines[0] - pack_ids1 = picking1.move_line_ids - pack_ids2 = picking2.move_line_ids - - assert pack_ids1.location_id.id == ref('stock_location_14') - assert set(pack_ids2.mapped('location_id.id')) == set([ref('stock_location_components'), ref('stock_location_14')]) - - # put the move lines from picking2 into picking1 - for pack_id2 in pack_ids2: - new_pack_id1 = pack_id2.copy(default={'picking_id': picking1.id, 'move_id': move1.id}) - new_pack_id1.qty_done = new_pack_id1.product_qty - new_pack_id1.with_context(bypass_reservation_update=True).product_uom_qty = 0 - - new_move_lines = picking1.move_line_ids.filtered(lambda p: p.qty_done) - assert sum(new_move_lines.mapped('product_qty')) == 0 - assert sum(new_move_lines.mapped('qty_done')) == 5 - assert set(new_move_lines.mapped('location_id.id')) == set([ref('stock_location_components'), ref('stock_location_14')]) - - # put the move line from picking1 into picking2 - new_pack_id2 = pack_ids1.copy(default={'picking_id': picking2.id, 'move_id': move2.id}) - new_pack_id2.qty_done = new_pack_id2.product_qty - new_pack_id2.with_context(bypass_reservation_update=True).product_uom_qty = 0 - - new_move_lines = picking2.move_line_ids.filtered(lambda p: p.qty_done) - assert len(new_move_lines) == 1 - assert sum(new_move_lines.mapped('product_qty')) == 0 - assert sum(new_move_lines.mapped('qty_done')) == 5 - assert new_move_lines.location_id.id == ref('stock_location_14') -- - Process this picking -- - !python {model: stock.picking, id: delivery_order_wise1}: | - self.do_transfer() -- - Check there was no negative quant created by this picking -- - !python {model: stock.quant, id: False}: | - records = self.search([('product_id','=',ref('product_wise')), ('quantity', '<', 0.0), ('location_id.id', '=', - ref('stock_location_stock'))]) - assert len(records.ids) == 0, 'This should not have created a negative quant' -- - Check the other delivery order has changed its state back to ready -- - !python {model: stock.picking, id: delivery_order_wise2}: | - assert self.state == 'assigned', "Delivery order 2 should be back in ready state" -- - Process the second picking -- - !python {model: stock.picking, id: delivery_order_wise2}: | - self.action_done() -- - Check all quants are in Customers and there are no negative quants anymore -- - !python {model: stock.quant, id: False}: | - records = self.search([('product_id','=',ref('product_wise')), ('location_id', '!=', ref('stock_location_suppliers'))]) - assert all([x.location_id.id==ref('stock_location_customers') and x.quantity > 0.0 for x in records]), "Negative quant or wrong location detected" \ No newline at end of file diff --git a/addons/stock/tests/__init__.py b/addons/stock/tests/__init__.py index a9a18e270e4..52f5e2c53b0 100644 --- a/addons/stock/tests/__init__.py +++ b/addons/stock/tests/__init__.py @@ -9,3 +9,7 @@ from . import test_quant from . import test_inventory from . import test_move from . import test_move2 +from . import test_packing +from . import test_packing_neg +from . import test_proc_rule +from . import test_wise_operator diff --git a/addons/stock/tests/test_packing.py b/addons/stock/tests/test_packing.py new file mode 100644 index 00000000000..11d6ed9f927 --- /dev/null +++ b/addons/stock/tests/test_packing.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests.common import TransactionCase + + +class TestPacking(TransactionCase): + + def test_packing(self): + + # Create a new stockable product + + product1 = self.env['product.product'].create({ + 'name': 'Nice product', + 'type': 'product', + 'categ_id': self.ref('product.product_category_1'), + 'list_price': 100.0, + 'standard_price': 70.0, + 'seller_ids': [(0, 0, { + 'delay': 1, + 'name': self.ref('base.res_partner_2'), + 'min_qty': 2.0, + })], + 'uom_id': self.ref('product.product_uom_unit'), + 'uom_po_id': self.ref('product.product_uom_unit'), + }) + + # Create an incoming picking for this product of 300 PCE from suppliers to stock + default_get_vals = self.env['stock.picking'].default_get(list(self.env['stock.picking'].fields_get())) + default_get_vals.update({ + 'name': 'Incoming picking', + 'partner_id': self.ref('base.res_partner_2'), + 'picking_type_id': self.ref('stock.picking_type_in'), + 'move_lines': [(0, 0, { + 'product_id': product1.id, + 'product_uom_qty': 300.00, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_stock'), + })], + }) + pick1 = self.env['stock.picking'].new(default_get_vals) + pick1.onchange_picking_type() + pick1.move_lines.onchange_product_id() + vals = pick1._convert_to_write(pick1._cache) + pick1 = self.env['stock.picking'].create(vals) + + # Confirm and assign picking + pick1.action_confirm() + pick1.action_assign() + + # Put 120 pieces on Pallet 1 (package), 120 pieces on Pallet 2 with lot A and 60 pieces on Pallet 3 + lot_a = self.env['stock.production.lot'].create({ + 'name': 'Lot A', + 'product_id': product1.id, + }) + + # create package + package1 = self.env['stock.quant.package'].create({'name': 'Pallet 1'}) + package2 = self.env['stock.quant.package'].create({'name': 'Pallet 2'}) + package3 = self.env['stock.quant.package'].create({'name': 'Pallet 3'}) + + # Create package for each line and assign it as result_package_id + # create pack operation + pick1.move_line_ids[0].write({'result_package_id': package1.id, 'qty_done': 120}) + new_pack1 = self.env['stock.move.line'].create({ + 'product_id': product1.id, + 'product_uom_id': self.ref('product.product_uom_unit'), + 'picking_id': pick1.id, + 'lot_id': lot_a.id, + 'qty_done': 120.0, + 'result_package_id': package2.id, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_stock') + }) + new_pack2 = self.env['stock.move.line'].create({ + 'product_id': product1.id, + 'product_uom_id': self.ref('product.product_uom_unit'), + 'picking_id': pick1.id, + 'result_package_id': package3.id, + 'qty_done': 60.0, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_stock') + }) + + # Transfer the receipt + pick1.action_done() + + # Check the system created 5 quants one with 120 pieces on pallet 1, + # one with 120 pieces on pallet 2 with lot A and 60 pieces on pallet 3 in stock + # and 1 with -180 pieces and another with -120 piece lot A in supplier + quants = self.env['stock.quant'].search([('product_id', '=', product1.id)]) + self.assertTrue(len(quants.ids) == 5, "The number of quants created is not correct") + for quant in quants: + if quant.package_id.name == 'Pallet 1': + self.assertTrue(quant.quantity == 120, "Should have 120 pieces on pallet 1") + elif quant.package_id.name == 'Pallet 2': + self.assertTrue(quant.quantity == 120, "Should have 120 pieces on pallet 2") + elif quant.package_id.name == 'Pallet 3': + self.assertTrue(quant.quantity == 60, "Should have 60 pieces on pallet 3") + + # Check there is no backorder or extra moves created + backorder = self.env['stock.picking'].search([('backorder_id', '=', pick1.id)]) + self.assertTrue(not backorder) + + # Check extra moves created + self.assertTrue(len(pick1.move_lines) == 1) + + # Make a delivery order of 300 pieces to the customer + default_get_vals = self.env['stock.picking'].default_get(list(self.env['stock.picking'].fields_get())) + default_get_vals.update({ + 'name': 'outgoing picking', + 'partner_id': self.ref('base.res_partner_4'), + 'picking_type_id': self.ref('stock.picking_type_out'), + 'move_lines': [(0, 0, { + 'product_id': product1.id, + 'product_uom_qty': 300.00, + 'location_id': self.ref('stock.stock_location_stock'), + 'location_dest_id': self.ref('stock.stock_location_customers'), + })], + }) + delivery_order1 = self.env['stock.picking'].new(default_get_vals) + delivery_order1.onchange_picking_type() + delivery_order1.move_lines.onchange_product_id() + vals = delivery_order1._convert_to_write(delivery_order1._cache) + delivery_order1 = self.env['stock.picking'].create(vals) + + # Assign and confirm + delivery_order1.action_confirm() + delivery_order1.action_assign() + + # Instead of doing the 300 pieces, you decide to take pallet 1 (do not + # mention product in operation here) and 20 pieces from lot A and 10 pieces from pallet 3 + for rec in delivery_order1.move_line_ids: + if rec.package_id.name == 'Pallet 1': + rec.qty_done = 120 + rec.result_package_id = False + if rec.package_id.name == 'Pallet 2': + rec.qty_done = 20 + rec.result_package_id = False + if rec.package_id.name == 'Pallet 3': + rec.qty_done = 10 + rec.result_package_id = False + + # Process this picking + delivery_order1.action_done() + + # Check the quants that you have 100 pieces of pallet 2 and 50 pieces of pallet 3 in stock + records = self.env['stock.quant'].search([('product_id', '=', product1.id)]) + for rec in records: + if rec.package_id.name == 'Pallet 2' and rec.location_id.id == self.ref('stock.stock_location_stock'): + self.assertTrue(rec.quantity == 100, "Should have 100 pieces in stock on pallet 2, got " + str(rec.quantity)) + elif rec.package_id.name == 'Pallet 3' and rec.location_id.id == self.ref('stock.stock_location_stock'): + self.assertTrue(rec.quantity == 50, "Should have 50 pieces in stock on pallet 3") + else: + self.assertTrue(rec.location_id.id != self.ref('stock.stock_location_stock'), "Unrecognized quant in stock") + + # Check a backorder was created and on that backorder, prepare partial and process backorder + backorders = self.env['stock.picking'].search([('backorder_id', '=', delivery_order1.id)]) + self.assertTrue(backorders, "Backorder should have been created") + backorders.action_assign() + picking = backorders[0] + self.assertTrue(len(picking.move_line_ids) == 2, "Wrong number of pack operation") + for pack_op in picking.move_line_ids: + self.assertTrue(pack_op.package_id.name in ('Pallet 2', 'Pallet 3'), "Wrong pallet info in pack operation (%s found)" % (pack_op.package_id.name)) + if pack_op.package_id.name == 'Pallet 2': + self.assertTrue(pack_op.product_qty == 100) + pack_op.qty_done = 100 + elif pack_op.package_id.name == 'Pallet 3': + self.assertTrue(pack_op.product_qty == 50) + pack_op.qty_done = 50 + backorders.action_done() + + # Check there are still 0 pieces in stock + records = self.env['stock.quant'].search([('product_id', '=', product1.id), ('location_id', '=', self.ref('stock.stock_location_stock'))]) + total_qty = 0 + for rec in records: + total_qty += rec.quantity + self.assertTrue(total_qty == 0, "Total quantity in stock should be 0 as the backorder took everything out of stock") + self.assertTrue(product1.qty_available == 0, "Quantity available should be 0 too") diff --git a/addons/stock/tests/test_packing_neg.py b/addons/stock/tests/test_packing_neg.py new file mode 100644 index 00000000000..bbc369136b4 --- /dev/null +++ b/addons/stock/tests/test_packing_neg.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests.common import TransactionCase + + +class TestPackingNeg(TransactionCase): + + def test_packing_neg(self): + + # Create a new "negative" stockable product + product_neg = self.env['product.product'].create({ + 'name': 'Negative product', + 'type': 'product', + 'categ_id': self.ref('product.product_category_1'), + 'list_price': 100.0, + 'standard_price': 70.0, + 'seller_ids': [(0, 0, { + 'delay': 1, + 'name': self.ref('base.res_partner_2'), + 'min_qty': 2.0,})], + 'uom_id': self.ref('product.product_uom_unit'), + 'uom_po_id': self.ref('product.product_uom_unit'), + }) + + # Create an incoming picking for this product of 300 PCE from suppliers to stock + default_get_vals = self.env['stock.picking'].default_get(list(self.env['stock.picking'].fields_get())) + default_get_vals.update({ + 'name': 'Incoming picking (negative product)', + 'partner_id': self.ref('base.res_partner_2'), + 'picking_type_id': self.ref('stock.picking_type_in'), + 'move_lines': [(0, 0, { + 'product_id': product_neg.id, + 'product_uom_qty': 300.00, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_stock'), + })], + }) + pick_neg = self.env['stock.picking'].new(default_get_vals) + pick_neg.onchange_picking_type() + pick_neg.move_lines.onchange_product_id() + vals = pick_neg._convert_to_write(pick_neg._cache) + pick_neg = self.env['stock.picking'].create(vals) + + # Confirm and assign picking + pick_neg.action_confirm() + pick_neg.action_assign() + + # Put 120 pieces on Palneg 1 (package), 120 pieces on Palneg 2 with lot A and 60 pieces on Palneg 3 + # create lot A + lot_a = self.env['stock.production.lot'].create({'name': 'Lot neg', 'product_id': product_neg.id}) + # create package + package1 = self.env['stock.quant.package'].create({'name': 'Palneg 1'}) + package2 = self.env['stock.quant.package'].create({'name': 'Palneg 2'}) + package3 = self.env['stock.quant.package'].create({'name': 'Palneg 3'}) + # Create package for each line and assign it as result_package_id + # create pack operation + pick_neg.move_line_ids[0].write({'result_package_id': package1.id, 'qty_done': 120}) + new_pack1 = self.env['stock.move.line'].create({ + 'product_id': product_neg.id, + 'product_uom_id': self.ref('product.product_uom_unit'), + 'picking_id': pick_neg.id, + 'lot_id': lot_a.id, + 'qty_done': 120, + 'result_package_id': package2.id, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_stock') + }) + new_pack2 = self.env['stock.move.line'].create({ + 'product_id': product_neg.id, + 'product_uom_id': self.ref('product.product_uom_unit'), + 'picking_id': pick_neg.id, + 'result_package_id': package3.id, + 'qty_done': 60, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_stock') + }) + + # Transfer the receipt + pick_neg.do_transfer() + + # Make a delivery order of 300 pieces to the customer + default_get_vals = self.env['stock.picking'].default_get(list(self.env['stock.picking'].fields_get())) + default_get_vals.update({ + 'name': 'outgoing picking (negative product)', + 'partner_id': self.ref('base.res_partner_4'), + 'picking_type_id': self.ref('stock.picking_type_out'), + 'move_lines': [(0, 0, { + 'product_id': product_neg.id, + 'product_uom_qty': 300.00, + 'location_id': self.ref('stock.stock_location_stock'), + 'location_dest_id': self.ref('stock.stock_location_customers'), + })], + }) + delivery_order_neg = self.env['stock.picking'].new(default_get_vals) + delivery_order_neg.onchange_picking_type() + delivery_order_neg.move_lines.onchange_product_id() + vals = delivery_order_neg._convert_to_write(delivery_order_neg._cache) + delivery_order_neg = self.env['stock.picking'].create(vals) + + # Assign and confirm + delivery_order_neg.action_confirm() + delivery_order_neg.action_assign() + + # Instead of doing the 300 pieces, you decide to take pallet 1 (do not mention + # product in operation here) and 140 pieces from lot A/pallet 2 and 10 pieces from pallet 3 + + for rec in delivery_order_neg.move_line_ids: + if rec.package_id.name == 'Palneg 1': + rec.qty_done = rec.product_qty + rec.result_package_id = False + elif rec.package_id.name == 'Palneg 2' and rec.lot_id.name == 'Lot neg': + rec.write({ + 'qty_done': 140, + 'result_package_id': False, + }) + elif rec.package_id.name == 'Palneg 3': + rec.qty_done = 10 + rec.result_package_id = False + + # Process this picking + delivery_order_neg.do_transfer() + + # Check the quants that you have -20 pieces pallet 2 in stock, and a total quantity + # of 50 in stock from pallet 3 (should be 20+30, as it has been split by reservation) + records = self.env['stock.quant'].search([('product_id', '=', product_neg.id)]) + pallet_3_stock_qty = 0 + for rec in records: + if rec.package_id.name == 'Palneg 2' and rec.location_id.id == self.ref('stock.stock_location_stock'): + self.assertTrue(rec.quantity == -20, "Should have -20 pieces in stock on pallet 2. Got " + str(rec.quantity)) + self.assertTrue(rec.lot_id.name == 'Lot neg', "It should have kept its Lot") + elif rec.package_id.name == 'Palneg 3' and rec.location_id.id == self.ref('stock.stock_location_stock'): + pallet_3_stock_qty += rec.quantity + else: + self.assertTrue(rec.location_id.id != self.ref('stock.stock_location_stock'), "Unrecognized quant in stock") + self.assertEqual(pallet_3_stock_qty, 50, "Should have 50 pieces in stock on pallet 3") + + # Create a picking for reconciling the negative quant + default_get_vals = self.env['stock.picking'].default_get(list(self.env['stock.picking'].fields_get())) + default_get_vals.update({ + 'name': 'reconciling_delivery', + 'partner_id': self.ref('base.res_partner_4'), + 'picking_type_id': self.ref('stock.picking_type_in'), + 'move_lines': [(0, 0, { + 'product_id': product_neg.id, + 'product_uom_qty': 20.0, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_stock'), + })], + }) + delivery_reconcile = self.env['stock.picking'].new(default_get_vals) + delivery_reconcile.onchange_picking_type() + delivery_reconcile.move_lines.onchange_product_id() + vals = delivery_reconcile._convert_to_write(delivery_reconcile._cache) + delivery_reconcile = self.env['stock.picking'].create(vals) + + # Receive 20 products with lot neg in stock with a new incoming shipment that should be on pallet 2 + delivery_reconcile.action_confirm() + lot = self.env["stock.production.lot"].search([ + ('product_id', '=', product_neg.id), + ('name', '=', 'Lot neg')], limit=1) + pack = self.env["stock.quant.package"].search([('name', '=', 'Palneg 2')], limit=1) + delivery_reconcile.move_line_ids[0].write({'lot_id': lot.id, 'qty_done': 20.0, 'result_package_id': pack.id}) + delivery_reconcile.do_transfer() + + # Check the negative quant was reconciled + neg_quants = self.env['stock.quant'].search([ + ('product_id', '=', product_neg.id), + ('quantity', '<', 0), + ('location_id.id', '!=', self.ref('stock.stock_location_suppliers'))]) + self.assertTrue(len(neg_quants) == 0, "Negative quants should have been reconciled") diff --git a/addons/stock/tests/test_proc_rule.py b/addons/stock/tests/test_proc_rule.py new file mode 100644 index 00000000000..044236729c7 --- /dev/null +++ b/addons/stock/tests/test_proc_rule.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests.common import TransactionCase +from odoo.tools import mute_logger + + +class TestProcRule(TransactionCase): + + def test_proc_rule(self): + # Create a product route containing a procurement rule that will + # generate a move from Stock for every procurement created in Output + product_route = self.env['stock.location.route'].create({ + 'name': 'Stock -> output route', + 'product_selectable': True, + 'pull_ids': [(0, 0, { + 'name': 'Stock -> output rule', + 'action': 'move', + 'picking_type_id': self.ref('stock.picking_type_internal'), + 'location_src_id': self.ref('stock.stock_location_stock'), + 'location_id': self.ref('stock.stock_location_output'), + })], + }) + + # Set this route on `product.product_product_3` + self.env.ref('product.product_product_3').write({ + 'route_ids': [(4, product_route.id)]}) + + # Create Delivery Order of 10 `product.product_product_3` from Output -> Customer + default_get_vals = self.env['stock.picking'].default_get(list(self.env['stock.picking'].fields_get())) + default_get_vals.update({ + 'name': 'Delivery order for procurement', + 'partner_id': self.ref('base.res_partner_2'), + 'picking_type_id': self.ref('stock.picking_type_out'), + 'move_lines': [(0, 0, { + 'product_id': self.ref('product.product_product_3'), + 'product_uom_qty': 10.00, + 'procure_method': 'make_to_order', + })], + }) + pick_output = self.env['stock.picking'].new(default_get_vals) + pick_output.onchange_picking_type() + pick_output.move_lines.onchange_product_id() + pick_output.update({ + 'location_id': self.ref('stock.stock_location_output'), + 'location_dest_id': self.ref('stock.stock_location_customers'), + }) + vals = pick_output._convert_to_write(pick_output._cache) + pick_output = self.env['stock.picking'].create(vals) + + # Confirm delivery order. + pick_output.action_confirm() + + # I run the scheduler. + # Note: If purchase if already installed, the method _run_buy will be called due + # to the purchase demo data. As we update the stock module to run this test, the + # method won't be an attribute of stock.procurement at this moment. For that reason + # we mute the logger when running the scheduler. + with mute_logger('odoo.addons.stock.models.procurement'): + self.env['procurement.group'].run_scheduler() + + # Check that a picking was created from stock to output. + moves = self.env['stock.move'].search([ + ('product_id', '=', self.ref('product.product_product_3')), + ('location_id', '=', self.ref('stock.stock_location_stock')), + ('location_dest_id', '=', self.ref('stock.stock_location_output')), + ('move_dest_ids', 'in', [pick_output.move_lines[0].id]) + ]) + self.assertEqual(len(moves.ids), 1, "It should have created a picking from Stock to Output with the original picking as destination") diff --git a/addons/stock/tests/test_wise_operator.py b/addons/stock/tests/test_wise_operator.py new file mode 100644 index 00000000000..8e84c6285b0 --- /dev/null +++ b/addons/stock/tests/test_wise_operator.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests.common import TransactionCase + + +class TestWiseOperator(TransactionCase): + + def test_wise_operator(self): + + # Create a new stockable product + product_wise = self.env['product.product'].create({ + 'name': 'Wise Unit', + 'type': 'product', + 'categ_id': self.ref('product.product_category_1'), + 'uom_id': self.ref('product.product_uom_unit'), + 'uom_po_id': self.ref('product.product_uom_unit'), + }) + + picking_default_vals = self.env['stock.picking'].default_get(list(self.env['stock.picking'].fields_get())) + + # Create an incoming picking for this product of 10 PCE from suppliers to stock + vals = dict(picking_default_vals, **{ + 'name': 'Incoming picking (wise unit)', + 'partner_id': self.ref('base.res_partner_2'), + 'picking_type_id': self.ref('stock.picking_type_in'), + 'move_lines': [(0, 0, { + 'product_id': product_wise.id, + 'product_uom_qty': 10.00, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_stock'), + })], + }) + pick1_wise = self.env['stock.picking'].new(vals) + pick1_wise.onchange_picking_type() + pick1_wise.move_lines.onchange_product_id() + vals = pick1_wise._convert_to_write(pick1_wise._cache) + pick1_wise = self.env['stock.picking'].create(vals) + + # Confirm and assign picking and prepare partial + pick1_wise.action_confirm() + pick1_wise.action_assign() + + # Put 4 pieces in shelf1 and 6 pieces in shelf2 + package1 = self.env['stock.quant.package'].create({'name': 'Pack 1'}) + pick1_wise.move_line_ids[0].write({ + 'result_package_id': package1.id, + 'qty_done': 4, + 'location_dest_id': self.ref('stock.stock_location_components') + }) + new_pack1 = self.env['stock.move.line'].create({ + 'product_id': product_wise.id, + 'product_uom_id': self.ref('product.product_uom_unit'), + 'picking_id': pick1_wise.id, + 'qty_done': 6.0, + 'location_id': self.ref('stock.stock_location_suppliers'), + 'location_dest_id': self.ref('stock.stock_location_14') + }) + + # Transfer the receipt + pick1_wise.do_transfer() + + # Check the system created 3 quants + records = self.env['stock.quant'].search([('product_id', '=', product_wise.id)]) + self.assertEqual(len(records.ids), 3, "The number of quants created is not correct") + + # Make a delivery order of 5 pieces to the customer + vals = dict(picking_default_vals, **{ + 'name': 'outgoing picking 1 (wise unit)', + 'partner_id': self.ref('base.res_partner_4'), + 'picking_type_id': self.ref('stock.picking_type_out'), + 'move_lines': [(0, 0, { + 'product_id': product_wise.id, + 'product_uom_qty': 5.0, + 'location_id': self.ref('stock.stock_location_stock'), + 'location_dest_id': self.ref('stock.stock_location_customers'), + })], + }) + delivery_order_wise1 = self.env['stock.picking'].new(vals) + delivery_order_wise1.onchange_picking_type() + delivery_order_wise1.move_lines.onchange_product_id() + vals = delivery_order_wise1._convert_to_write(delivery_order_wise1._cache) + delivery_order_wise1 = self.env['stock.picking'].create(vals) + + # Assign and confirm + delivery_order_wise1.action_confirm() + delivery_order_wise1.action_assign() + self.assertEqual(delivery_order_wise1.state, 'assigned') + + # Make a delivery order of 5 pieces to the customer + vals = dict(picking_default_vals, **{ + 'name': 'outgoing picking 2 (wise unit)', + 'partner_id': self.ref('base.res_partner_4'), + 'picking_type_id': self.ref('stock.picking_type_out'), + 'move_lines': [(0, 0, { + 'product_id': product_wise.id, + 'product_uom_qty': 5.0, + 'location_id': self.ref('stock.stock_location_stock'), + 'location_dest_id': self.ref('stock.stock_location_customers'), + })], + }) + delivery_order_wise2 = self.env['stock.picking'].new(vals) + delivery_order_wise2.onchange_picking_type() + delivery_order_wise2.move_lines.onchange_product_id() + vals = delivery_order_wise2._convert_to_write(delivery_order_wise2._cache) + delivery_order_wise2 = self.env['stock.picking'].create(vals) + + # Assign and confirm + delivery_order_wise2.action_confirm() + delivery_order_wise2.action_assign() + self.assertEqual(delivery_order_wise2.state, 'assigned') + + # The operator is a wise guy and decides to do the opposite of what Odoo proposes. + # He uses the products reserved on picking 1 on picking 2 and vice versa + move1 = delivery_order_wise1.move_lines[0] + move2 = delivery_order_wise2.move_lines[0] + pack_ids1 = delivery_order_wise1.move_line_ids + pack_ids2 = delivery_order_wise2.move_line_ids + + self.assertEqual(pack_ids1.location_id.id, self.ref('stock.stock_location_14')) + self.assertEqual(set(pack_ids2.mapped('location_id.id')), set([ + self.ref('stock.stock_location_components'), + self.ref('stock.stock_location_14')])) + + # put the move lines from delivery_order_wise2 into delivery_order_wise1 + for pack_id2 in pack_ids2: + new_pack_id1 = pack_id2.copy(default={'picking_id': delivery_order_wise1.id, 'move_id': move1.id}) + new_pack_id1.qty_done = new_pack_id1.product_qty + new_pack_id1.with_context(bypass_reservation_update=True).product_uom_qty = 0 + + new_move_lines = delivery_order_wise1.move_line_ids.filtered(lambda p: p.qty_done) + self.assertEqual(sum(new_move_lines.mapped('product_qty')), 0) + self.assertEqual(sum(new_move_lines.mapped('qty_done')), 5) + self.assertEqual(set(new_move_lines.mapped('location_id.id')), set([ + self.ref('stock.stock_location_components'), + self.ref('stock.stock_location_14')])) + + # put the move line from delivery_order_wise1 into delivery_order_wise2 + new_pack_id2 = pack_ids1.copy(default={'picking_id': delivery_order_wise2.id, 'move_id': move2.id}) + new_pack_id2.qty_done = new_pack_id2.product_qty + new_pack_id2.with_context(bypass_reservation_update=True).product_uom_qty = 0 + + new_move_lines = delivery_order_wise2.move_line_ids.filtered(lambda p: p.qty_done) + self.assertEqual(len(new_move_lines), 1) + self.assertEqual(sum(new_move_lines.mapped('product_qty')), 0) + self.assertEqual(sum(new_move_lines.mapped('qty_done')), 5) + self.assertEqual(new_move_lines.location_id.id, self.ref('stock.stock_location_14')) + + # Process this picking + delivery_order_wise1.do_transfer() + + # Check there was no negative quant created by this picking + + records = self.env['stock.quant'].search([ + ('product_id', '=', product_wise.id), + ('quantity', '<', 0.0), + ('location_id.id', '=', self.ref('stock.stock_location_stock'))]) + self.assertEqual(len(records.ids), 0, 'This should not have created a negative quant') + + # Check the other delivery order has changed its state back to ready + self.assertEqual(delivery_order_wise2.state, 'assigned', "Delivery order 2 should be back in ready state") + + # Process the second picking + delivery_order_wise2.action_done() + + # Check all quants are in Customers and there are no negative quants anymore + records = self.env['stock.quant'].search([ + ('product_id', '=', product_wise.id), + ('location_id', '!=', self.ref('stock.stock_location_suppliers'))]) + self.assertTrue(all([x.location_id.id == self.ref('stock.stock_location_customers') and x.quantity > 0.0 for x in records]), + "Negative quant or wrong location detected")