19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:47 +01:00
parent accf5918df
commit 6e65e8c877
688 changed files with 225434 additions and 199401 deletions

View file

@ -1,36 +1,36 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from unittest import skip
from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
from odoo import Command
from odoo.tests import tagged, Form
@tagged('post_install', '-at_install')
@skip('Temporary to fast merge new valuation')
class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
def setUpClass(cls):
super().setUpClass()
categ_form = Form(cls.env['product.category'])
categ_form.name = 'fifo auto'
categ_form.parent_id = cls.env.ref('product.product_category_all')
categ_form.property_cost_method = 'fifo'
categ_form.property_valuation = 'real_time'
cls.categ_fifo_auto = categ_form.save()
categ_form = Form(cls.env['product.category'])
categ_form.name = 'avco auto'
categ_form.parent_id = cls.env.ref('product.product_category_all')
categ_form.property_cost_method = 'average'
categ_form.property_valuation = 'real_time'
cls.categ_avco_auto = categ_form.save()
cls.dropship_route = cls.env.ref('stock_dropshipping.route_drop_shipping')
cls.dropship_subcontractor_route = cls.env.ref('mrp_subcontracting_dropshipping.route_subcontracting_dropshipping')
(cls.product_a | cls.product_b).is_storable = True
(cls.product_a | cls.product_b).type = 'product'
cls.dropship_route = cls.env.ref('stock_dropshipping.route_drop_shipping')
cls.bom_a = cls.env['mrp.bom'].create({
'product_tmpl_id': cls.product_a.product_tmpl_id.id,
@ -54,7 +54,7 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
all_amls_ids = self.env['account.move.line'].search_read([], ['id'])
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
self.env.user.write({'groups_id': [(4, grp_multi_loc.id)]})
self.env.user.write({'group_ids': [(4, grp_multi_loc.id)]})
(self.product_a | self.product_b).categ_id = self.categ_fifo_auto
self.product_b.standard_price = 10
@ -74,18 +74,18 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
'name': self.product_a.name,
'product_qty': 2.0,
'price_unit': 100,
'taxes_id': False,
'tax_ids': False,
})],
})
po.button_confirm()
delivery = po.picking_ids
res = delivery.button_validate()
Form(self.env['stock.immediate.transfer'].with_context(res['context'])).save().process()
delivery.button_validate()
stock_in_acc_id = self.categ_fifo_auto.property_stock_account_input_categ_id.id
stock_out_acc_id = self.categ_fifo_auto.property_stock_account_output_categ_id.id
stock_valu_acc_id = self.categ_fifo_auto.property_stock_valuation_account_id.id
stock_cop_acc_id = self.categ_fifo_auto.property_stock_account_production_cost_id.id
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
all_amls_ids += amls.ids
@ -94,26 +94,25 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 20.0},
{'account_id': stock_out_acc_id, 'product_id': self.product_a.id, 'debit': 20.0, 'credit': 0.0},
# Receipt from subcontractor
{'account_id': stock_in_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 220.0},
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 220.0, 'credit': 0.0},
{'account_id': stock_in_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 200.0},
{'account_id': stock_cop_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 20.0},
# Delivery to subcontractor
{'account_id': stock_valu_acc_id, 'product_id': self.product_b.id, 'debit': 0.0, 'credit': 20.0},
{'account_id': stock_out_acc_id, 'product_id': self.product_b.id, 'debit': 20.0, 'credit': 0.0},
{'account_id': stock_cop_acc_id, 'product_id': self.product_b.id, 'debit': 20.0, 'credit': 0.0},
# Initial dropshipped value
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 200.0},
{'account_id': stock_out_acc_id, 'product_id': self.product_a.id, 'debit': 200.0, 'credit': 0.0},
])
# return to subcontracting location
sbc_location = self.env.company.subcontracting_location_id
return_form = Form(self.env['stock.return.picking'].with_context(active_id=delivery.id, active_model='stock.picking'))
return_form.location_id = sbc_location
with return_form.product_return_moves.edit(0) as line:
line.quantity = 1
return_wizard = return_form.save()
return_id, _ = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_id)
return_picking.move_ids.quantity_done = 1
return_picking = return_wizard._create_return()
return_picking.move_ids.quantity = 1
return_picking.move_ids.picked = True
return_picking.button_validate()
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
@ -126,15 +125,14 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
# return to stock location
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
stock_location = warehouse.lot_stock_id
stock_location.return_location = True
return_form = Form(self.env['stock.return.picking'].with_context(active_id=delivery.id, active_model='stock.picking'))
return_form.location_id = stock_location
with return_form.product_return_moves.edit(0) as line:
line.quantity = 1
return_wizard = return_form.save()
return_id, _ = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_id)
return_picking.move_ids.quantity_done = 1
return_picking = return_wizard._create_return()
return_picking.move_ids.quantity = 1
return_picking.move_ids.picked = True
return_picking.location_dest_id = stock_location
return_picking.button_validate()
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
@ -157,7 +155,7 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
comp_product = self.product_b
comp_product.write({
'categ_id': self.categ_avco_auto.id,
'route_ids': [(4, self.dropship_subcontractor_route.id)],
'route_ids': [(4, self.dropship_route.id)],
})
self.env['product.supplierinfo'].create({
@ -171,11 +169,11 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
'price': 1,
})
sale_order = self.env['sale.order'].create({
sale_order = self.env['sale.order'].sudo().create({
'partner_id': self.partner_a.id,
'order_line': [(0, 0, {
'product_id': final_product.id,
'route_id': self.dropship_route.id,
'route_ids': [Command.link(self.dropship_route.id)],
'product_uom_qty': 100,
})],
})
@ -183,12 +181,12 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
purchase_order = sale_order._get_purchase_orders()[0]
purchase_order.button_confirm()
dropship_transfer = purchase_order.picking_ids[0]
dropship_transfer.move_ids[0].quantity_done = 50
dropship_transfer.move_ids[0].quantity = 50
dropship_transfer.with_context(cancel_backorder=False)._action_done()
account_move_1 = sale_order._create_invoices()
account_move_1.action_post()
dropship_backorder = dropship_transfer.backorder_ids[0]
dropship_backorder.action_set_quantities_to_reservation()
dropship_backorder.move_ids[0].quantity = 50
dropship_backorder._action_done()
account_move_2 = sale_order._create_invoices()
account_move_2.action_post()
@ -198,11 +196,11 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
[
# DS/01
{'reference': dropship_transfer.name, 'quantity': -50, 'value': -500},
{'reference': dropship_transfer.move_ids.move_orig_ids[0].name, 'quantity': 50, 'value': 8500},
{'reference': dropship_transfer.move_ids.move_orig_ids[0].reference, 'quantity': 50, 'value': 8500},
{'reference': dropship_transfer.name, 'quantity': 0, 'value': -8000},
# DS/02 - backorder
{'reference': dropship_backorder.name, 'quantity': -50, 'value': -500},
{'reference': dropship_backorder.move_ids.move_orig_ids[1].name, 'quantity': 50, 'value': 8500},
{'reference': dropship_backorder.move_ids.move_orig_ids[0].reference, 'quantity': 50, 'value': 8500},
{'reference': dropship_backorder.name, 'quantity': 0, 'value': -8000},
]
)
@ -216,13 +214,13 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
product_c = self.env['product.product'].create({
'name': 'product_c',
'uom_id': self.env.ref('uom.product_uom_dozen').id,
'uom_po_id': self.env.ref('uom.product_uom_dozen').id,
'lst_price': 120.0,
'standard_price': 100.0,
'property_account_income_id': self.copy_account(self.company_data['default_account_revenue']).id,
'property_account_expense_id': self.copy_account(self.company_data['default_account_expense']).id,
'taxes_id': [Command.set((self.tax_sale_a + self.tax_sale_b).ids)],
'supplier_taxes_id': [Command.set((self.tax_purchase_a + self.tax_purchase_b).ids)],
'is_storable': True
})
kit_bom = self.env['mrp.bom'].create({
'product_tmpl_id': kit_final_prod.product_tmpl_id.id,
@ -230,13 +228,18 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
'product_qty': 1.0,
'type': 'phantom',
})
kit_bom.bom_line_ids = [(0, 0, {
'product_id': self.product_b.id,
'product_qty': 4,
}), (0, 0, {
'product_id': product_c.id,
'product_qty': 2,
})]
# bom line of product_c is expressed in unit to check the uom conversion (24 unit should give the same result as 2 dozens)
kit_bom.bom_line_ids = [
Command.create({
'product_id': self.product_b.id,
'product_qty': 4,
}),
Command.create({
'product_id': product_c.id,
'product_qty': 24,
'product_uom_id': self.env.ref('uom.product_uom_unit').id
}),
]
self.env['product.supplierinfo'].create({
'product_id': self.product_b.id,
@ -248,18 +251,18 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
'partner_id': self.partner_a.id,
'price': 100,
})
self.product_b.standard_price = 10
(kit_final_prod + self.product_b).categ_id.write({
'property_cost_method': 'fifo',
'property_valuation': 'real_time',
})
sale_order = self.env['sale.order'].create({
sale_order = self.env['sale.order'].sudo().create({
'partner_id': self.partner_b.id,
'order_line': [(0, 0, {
'order_line': [Command.create({
'price_unit': 900,
'product_id': kit_final_prod.id,
'route_id': self.dropship_route.id,
'route_ids': [Command.link(self.dropship_route.id)],
'product_uom_qty': 2.0,
})],
})
@ -267,24 +270,31 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
purchase_order = sale_order._get_purchase_orders()[0]
purchase_order.button_confirm()
dropship_transfer = purchase_order.picking_ids[0]
dropship_transfer.move_ids[0].quantity_done = 2.0
dropship_transfer.button_validate()
account_move = sale_order._create_invoices()
account_move.action_post()
purchase_order.action_create_invoice()
bill = purchase_order.invoice_ids
bill.action_post()
invoice = sale_order._create_invoices()
invoice.action_post()
# Each product_a should cost:
# 4x product_b = 160 * 4 = 640 +
# 2x product_c = 100 * 2 = 200
# = 840
self.assertRecordValues(
account_move.line_ids,
[
{'name': 'product_a', 'debit': 0.0, 'credit': 1800.0},
{'name': 'Tax 15% (Copy)', 'debit': 0.0, 'credit': 270.0},
{'name': account_move.name, 'debit': 621.0, 'credit': 0.0},
{'name': account_move.name, 'debit': 1449.0, 'credit': 0.0},
{'name': 'product_a', 'debit': 0.0, 'credit': 840 * 2},
{'name': 'product_a', 'debit': 840 * 2, 'credit': 0.0},
]
)
# Since the kit is dropshipped, the expense should be recorded in the PO directly, as it never enter the stock.
self.assertRecordValues(bill.line_ids.sorted('balance'), [
{'name': False, 'account_name': 'Account Payable', 'debit': 0.0, 'credit': 2184.0},
{'name': '15%', 'account_name': 'Tax Paid', 'debit': 252.0, 'credit': 0.0},
{'name': '15% (copy)', 'account_name': 'Tax Paid', 'debit': 252.0, 'credit': 0.0},
{'name': 'product_c', 'account_name': 'Expenses', 'debit': 400.0, 'credit': 0.0},
{'name': 'product_b', 'account_name': 'Expenses', 'debit': 1280.0, 'credit': 0.0},
])
self.assertRecordValues(invoice.line_ids.sorted('balance'), [
{'name': 'product_a', 'account_name': 'Product Sales', 'debit': 0.0, 'credit': 1800.0},
{'name': '15% (copy)', 'account_name': 'Tax Received', 'debit': 0.0, 'credit': 270.0},
{'name': f'{sale_order.name} - {invoice.name} installment #1', 'account_name': 'Account Receivable (copy)', 'debit': 621.0, 'credit': 0.0},
{'name': f'{sale_order.name} - {invoice.name} installment #2', 'account_name': 'Account Receivable (copy)', 'debit': 1449.0, 'credit': 0.0},
])

View file

@ -1,12 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
from odoo.tests import Form
from odoo.tests import Form, tagged
from odoo.addons.mrp_subcontracting.tests.common import TestMrpSubcontractingCommon
from odoo.addons.stock_account.tests.common import TestStockValuationCommon
class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
@tagged('post_install', '-at_install')
class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon, TestStockValuationCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.comp1.seller_ids = [Command.create({'partner_id': cls.vendor.id})]
cls.warehouse.subcontracting_to_resupply = True
def test_mrp_subcontracting_dropshipping_1(self):
""" Mark the subcontracted product with the route dropship and add the
@ -17,36 +24,21 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
- Delivery for the component to the subcontractor for the specified wh.
- Po created for the component.
"""
# self.warehouse.manufacture_pull_id.route_id.write({'sequence': 20})
self.env.ref('stock.route_warehouse0_mto').active = True
mto_route = self.env['stock.route'].search([('name', '=', 'Replenish on Order (MTO)')])
resupply_route = self.env['stock.route'].search([('name', '=', 'Resupply Subcontractor on Order')])
buy_route = self.env['stock.route'].search([('name', '=', 'Buy')])
dropship_route = self.env['stock.route'].search([('name', '=', 'Dropship')])
self.comp2.write({'route_ids': [(4, buy_route.id), (4, mto_route.id), (4, resupply_route.id)]})
self.finished.write({'route_ids': [(4, dropship_route.id)]})
warehouse = self.env['stock.warehouse'].create({
'name': 'Warehouse For subcontract',
'code': 'WFS'
})
self.env['product.supplierinfo'].create({
'product_tmpl_id': self.finished.product_tmpl_id.id,
'partner_id': self.subcontractor_partner1.id
})
self.comp1.route_ids = [Command.link(mto_route.id)]
self.finished.route_ids = [Command.link(dropship_route.id)]
partner = self.env['res.partner'].create({
'name': 'Toto'
})
self.env['product.supplierinfo'].create({
'product_tmpl_id': self.comp2.product_tmpl_id.id,
'partner_id': partner.id
})
# Create a receipt picking from the subcontractor
so_form = Form(self.env['sale.order'])
so_form = Form(self.env['sale.order'].sudo())
so_form.partner_id = partner
so_form.warehouse_id = warehouse
so_form.warehouse_id = self.warehouse
with so_form.order_line.new() as line:
line.product_id = self.finished
line.product_uom_qty = 1
@ -66,16 +58,16 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(picking_finished.state, 'assigned')
picking_delivery = self.env['stock.move'].search([
('product_id', '=', self.comp2.id),
('location_id', '=', warehouse.lot_stock_id.id),
('product_id', '=', self.comp1.id),
('location_id', '=', self.stock_location.id),
('location_dest_id', '=', self.subcontractor_partner1.property_stock_subcontractor.id),
]).picking_id
self.assertTrue(picking_delivery)
self.assertEqual(picking_delivery.state, 'waiting')
po = self.env['purchase.order.line'].search([
('product_id', '=', self.comp2.id),
('partner_id', '=', partner.id),
('product_id', '=', self.comp1.id),
('partner_id', '=', self.vendor.id),
]).order_id
self.assertTrue(po)
@ -84,21 +76,10 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
Purchase 1 BOM to the subcontractor. Confirm the purchase and change the purchased quantity to 2.
Check that 2 components are delivered to the subcontractor
"""
# Tick "resupply subconractor on order on component"
self.bom.bom_line_ids = [(5, 0, 0)]
self.bom.bom_line_ids = [(0, 0, {'product_id': self.comp1.id, 'product_qty': 1})]
resupply_sub_on_order_route = self.env['stock.route'].search([('name', '=', 'Resupply Subcontractor on Order')])
(self.comp1).write({'route_ids': [(4, resupply_sub_on_order_route.id, None)]})
# Create a supplier and set it to component
vendor = self.env['res.partner'].create({'name': 'AAA', 'email': 'from.test@example.com'})
self.env['product.supplierinfo'].create({
'partner_id': vendor.id,
'price': 50,
})
self.comp1.write({'seller_ids': [(0, 0, {'partner_id': vendor.id, 'product_code': 'COMP1'})]})
# Purchase 1 BOM to the subcontractor
po = Form(self.env['purchase.order'])
po.partner_id = self.subcontractor_partner1
po.picking_type_id = self.warehouse.in_type_id
with po.order_line.new() as po_line:
po_line.product_id = self.finished
po_line.product_qty = 1
@ -112,23 +93,23 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
# Check that 1 delivery with 1 component for the subcontractor has been created
picking_delivery = mo.picking_ids
wh = picking_delivery.picking_type_id.warehouse_id
origin = picking_delivery.origin
self.assertEqual(len(picking_delivery), 1)
self.assertEqual(len(picking_delivery.move_ids_without_package), 1)
self.assertEqual(len(picking_delivery.move_ids), 2)
self.assertEqual(picking_delivery.picking_type_id, wh.subcontracting_resupply_type_id)
self.assertEqual(picking_delivery.partner_id, self.subcontractor_partner1)
self.assertEqual(picking_delivery.partner_id, self.subcontractor_partner1.parent_id)
# Change the purchased quantity to 2
po.order_line.write({'product_qty': 2})
# Check that a single delivery with the two components for the subcontractor have been created
picking_deliveries = self.env['stock.picking'].search([('origin', '=', origin)])
mo2 = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]) - mo
picking_deliveries = mo2.picking_ids
self.assertEqual(len(picking_deliveries), 1)
self.assertEqual(picking_deliveries.picking_type_id, wh.subcontracting_resupply_type_id)
self.assertEqual(picking_deliveries.partner_id, self.subcontractor_partner1)
self.assertEqual(picking_deliveries.partner_id, self.subcontractor_partner1.parent_id)
self.assertTrue(picking_deliveries.state != 'cancel')
move1 = picking_deliveries.move_ids_without_package
move1 = picking_deliveries.move_ids[0]
self.assertEqual(move1.product_id, self.comp1)
self.assertEqual(move1.product_uom_qty, 2)
self.assertEqual(move1.product_uom_qty, 1)
def test_dropshipped_component_and_sub_location(self):
"""
@ -138,53 +119,29 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
This test ensures that the PO that brings the component to the subcontractor has a correct
destination address
"""
subcontract_location = self.env.company.subcontracting_location_id
subcontract_location = self.subcontractor_partner1.property_stock_subcontractor
self.subcontractor_partner1.parent_id = False
sub_location = self.env['stock.location'].create({
'name': 'Super Location',
'location_id': subcontract_location.id,
'is_subcontracting_location': True,
})
dropship_subcontractor_route = self.env['stock.route'].search([('name', '=', 'Dropship Subcontractor on Order')])
subcontractor, vendor = self.env['res.partner'].create([
{'name': 'SuperSubcontractor', 'property_stock_subcontractor': sub_location.id},
{'name': 'SuperVendor'},
])
p_finished, p_compo = self.env['product.product'].create([{
'name': 'Finished Product',
'type': 'product',
'seller_ids': [(0, 0, {'partner_id': subcontractor.id})],
}, {
'name': 'Component',
'type': 'consu',
'seller_ids': [(0, 0, {'partner_id': vendor.id})],
'route_ids': [(6, 0, dropship_subcontractor_route.ids)]
}])
self.env['mrp.bom'].create({
'product_tmpl_id': p_finished.product_tmpl_id.id,
'product_qty': 1,
'type': 'subcontract',
'subcontractor_ids': [(6, 0, subcontractor.ids)],
'bom_line_ids': [
(0, 0, {'product_id': p_compo.id, 'product_qty': 1}),
],
})
dropship_route = self.env['stock.route'].search([('name', '=', 'Dropship')])
self.comp1.route_ids = [Command.link(dropship_route.id)]
self.subcontractor_partner1.property_stock_subcontractor = sub_location
subcontract_po = self.env['purchase.order'].create({
"partner_id": subcontractor.id,
"order_line": [(0, 0, {
'product_id': p_finished.id,
'name': p_finished.name,
"partner_id": self.subcontractor_partner1.id,
"picking_type_id": self.warehouse.in_type_id.id,
"order_line": [Command.create({
'product_id': self.finished.id,
'name': self.finished.name,
'product_qty': 1.0,
})],
})
subcontract_po.button_confirm()
dropship_po = self.env['purchase.order'].search([('partner_id', '=', vendor.id)])
self.assertEqual(dropship_po.dest_address_id, subcontractor)
dropship_po = self.env['purchase.order'].search([('partner_id', '=', self.vendor.id)])
self.assertEqual(dropship_po.dest_address_id, self.subcontractor_partner1)
def test_po_to_customer(self):
"""
@ -194,55 +151,33 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
the supplier as destination
"""
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
self.env.user.write({'groups_id': [(4, grp_multi_loc.id)]})
subcontractor, client = self.env['res.partner'].create([
{'name': 'SuperSubcontractor'},
{'name': 'SuperClient'},
])
p_finished, p_compo = self.env['product.product'].create([{
'name': 'Finished Product',
'type': 'product',
'seller_ids': [(0, 0, {'partner_id': subcontractor.id})],
}, {
'name': 'Component',
'type': 'consu',
}])
bom = self.env['mrp.bom'].create({
'product_tmpl_id': p_finished.product_tmpl_id.id,
'product_qty': 1,
'type': 'subcontract',
'subcontractor_ids': [(6, 0, subcontractor.ids)],
'bom_line_ids': [
(0, 0, {'product_id': p_compo.id, 'product_qty': 1}),
],
})
self.env.user.write({'group_ids': [(4, grp_multi_loc.id)]})
dropship_picking_type = self.env['stock.picking.type'].search([
('company_id', '=', self.env.company.id),
('default_location_src_id.usage', '=', 'supplier'),
('default_location_dest_id.usage', '=', 'customer'),
], limit=1, order='sequence')
], order='sequence')
po = self.env['purchase.order'].create({
"partner_id": subcontractor.id,
"partner_id": self.subcontractor_partner1.id,
"picking_type_id": dropship_picking_type.id,
"dest_address_id": client.id,
"dest_address_id": self.vendor.id,
"order_line": [(0, 0, {
'product_id': p_finished.id,
'name': p_finished.name,
'product_id': self.finished.id,
'name': self.finished.name,
'product_qty': 2.0,
})],
})
po.button_confirm()
mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)])
self.assertEqual(mo.picking_type_id, self.warehouse.subcontracting_type_id)
mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)])
wos = self.env['stock.warehouse'].search([('company_id', '=', self.company.id)])
self.assertIn(mo.picking_type_id, wos.subcontracting_type_id)
delivery = po.picking_ids
delivery.move_line_ids.qty_done = 2.0
delivery.move_line_ids.quantity = 2.0
delivery.move_ids.picked = True
delivery.button_validate()
self.assertEqual(delivery.state, 'done')
@ -250,34 +185,29 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(po.order_line.qty_received, 2)
# return 1 x P_finished to the stock location
stock_location = self.warehouse.lot_stock_id
stock_location.return_location = True
return_form = Form(self.env['stock.return.picking'].with_context(active_ids=delivery.ids, active_id=delivery.id, active_model='stock.picking'))
with return_form.product_return_moves.edit(0) as line:
line.quantity = 1.0
return_form.location_id = stock_location
return_wizard = return_form.save()
return_picking_id, _pick_type_id = return_wizard._create_returns()
delivery_return01 = self.env['stock.picking'].browse(return_picking_id)
delivery_return01.move_line_ids.qty_done = 1.0
delivery_return01 = return_wizard._create_return()
delivery_return01.move_line_ids.quantity = 1.0
delivery_return01.move_ids.picked = True
delivery_return01.location_dest_id = self.warehouse.lot_stock_id
delivery_return01.button_validate()
self.assertEqual(delivery_return01.state, 'done')
self.assertEqual(p_finished.qty_available, 1, 'One product has been returned to the stock location, so it should be available')
self.assertEqual(self.finished.qty_available, 1, 'One product has been returned to the stock location, so it should be available')
self.assertEqual(po.order_line.qty_received, 2, 'One product has been returned to the stock location, so we should still consider it as received')
# return 1 x P_finished to the supplier location
supplier_location = dropship_picking_type.default_location_src_id
return_form = Form(self.env['stock.return.picking'].with_context(active_ids=delivery.ids, active_id=delivery.id, active_model='stock.picking'))
with return_form.product_return_moves.edit(0) as line:
line.quantity = 1.0
return_form.location_id = supplier_location
return_wizard = return_form.save()
return_picking_id, _pick_type_id = return_wizard._create_returns()
delivery_return02 = self.env['stock.picking'].browse(return_picking_id)
delivery_return02.move_line_ids.qty_done = 1.0
delivery_return02 = return_wizard._create_return()
delivery_return02.location_dest_id = dropship_picking_type.default_location_src_id
delivery_return02.move_line_ids.quantity = 1.0
delivery_return02.move_ids.picked = True
delivery_return02.button_validate()
self.assertEqual(delivery_return02.state, 'done')
@ -289,136 +219,68 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
also a component of another subcontracted product. The picking type of
the PO is 'Dropship' and the delivery address is the other subcontractor
"""
subcontractor, super_subcontractor = self.env['res.partner'].create([
{'name': 'Subcontractor'},
{'name': 'SuperSubcontractor'},
])
subcontractor = self.env['res.partner'].create({'name': 'Subcontractor'})
super_product, product, component = self.env['product.product'].create([{
'name': 'Super Product',
'type': 'product',
'seller_ids': [(0, 0, {'partner_id': super_subcontractor.id})],
}, {
'name': 'Product',
'type': 'product',
'seller_ids': [(0, 0, {'partner_id': subcontractor.id})],
}, {
component = self.env['product.product'].create({
'name': 'Component',
'type': 'consu',
}])
})
_, bom_product = self.env['mrp.bom'].create([{
'product_tmpl_id': super_product.product_tmpl_id.id,
'product_qty': 1,
'type': 'subcontract',
'subcontractor_ids': [(6, 0, super_subcontractor.ids)],
'bom_line_ids': [
(0, 0, {'product_id': product.id, 'product_qty': 1}),
],
}, {
'product_tmpl_id': product.product_tmpl_id.id,
bom_product = self.env['mrp.bom'].create({
'product_tmpl_id': self.comp1.product_tmpl_id.id,
'product_qty': 1,
'type': 'subcontract',
'subcontractor_ids': [(6, 0, subcontractor.ids)],
'bom_line_ids': [
(0, 0, {'product_id': component.id, 'product_qty': 1}),
],
}])
})
po = self.env['purchase.order'].create({
"partner_id": subcontractor.id,
"picking_type_id": self.env.company.dropship_subcontractor_pick_type_id.id,
"dest_address_id": super_subcontractor.id,
"dest_address_id": self.subcontractor_partner1.id,
"order_line": [(0, 0, {
'product_id': product.id,
'name': product.name,
'product_id': self.comp1.id,
'name': self.comp1.name,
'product_qty': 1.0,
})],
})
po.button_confirm()
mo = self.env['mrp.production'].search([('bom_id', '=', bom_product.id)])
self.assertEqual(mo.picking_type_id, self.warehouse.subcontracting_type_id)
wos = self.env['stock.warehouse'].search([('company_id', '=', self.company.id)])
self.assertIn(mo.picking_type_id, wos.subcontracting_type_id)
delivery = po.picking_ids
self.assertEqual(delivery.location_dest_id, super_subcontractor.property_stock_subcontractor)
self.assertEqual(delivery.location_dest_id, self.subcontractor_partner1.property_stock_subcontractor)
self.assertTrue(delivery.is_dropship)
delivery.move_line_ids.qty_done = 1.0
delivery.move_line_ids.quantity = 1.0
delivery.move_ids.picked = True
delivery.button_validate()
self.assertEqual(po.order_line.qty_received, 1.0)
def test_two_boms_same_component_supplier(self):
"""
The "Dropship Subcontractor" route is modified: the propagation of the
buy rule is set to "Leave Empty".
Two subcontracted products (different subcontractor) that use the same
component. The component has its own supplier. Confirm one PO for each
subcontrated product. It should generate two PO from component's
supplier to each subcontractor.
"""
dropship_subcontractor_route = self.env.ref('mrp_subcontracting_dropshipping.route_subcontracting_dropshipping')
dropship_subcontractor_route.rule_ids.filtered(lambda r: r.action == 'buy').group_propagation_option = 'none'
subcontractor01, subcontractor02, component_supplier = self.env['res.partner'].create([{
'name': 'Super Partner %d' % i
} for i in range(3)])
product01, product02, component = self.env['product.product'].create([{
'name': name,
'type': 'product',
'seller_ids': [(0, 0, {'partner_id': vendor.id})],
'route_ids': [(6, 0, routes)],
} for name, vendor, routes in [
('SuperProduct 01', subcontractor01, []),
('SuperProduct 02', subcontractor02, []),
('Component', component_supplier, dropship_subcontractor_route.ids),
]])
self.env['mrp.bom'].create([{
'product_tmpl_id': finished.product_tmpl_id.id,
'type': 'subcontract',
'subcontractor_ids': [(4, subcontractor.id)],
'bom_line_ids': [(0, 0, {'product_id': component.id})]
} for finished, subcontractor in [
(product01, subcontractor01),
(product02, subcontractor02),
]])
for (partner, product) in [(subcontractor01, product01), (subcontractor02, product02)]:
po_form = Form(self.env['purchase.order'])
po_form.partner_id = partner
with po_form.order_line.new() as line:
line.product_id = product
po = po_form.save()
po.button_confirm()
supplier_orders = self.env['purchase.order'].search([('partner_id', '=', component_supplier.id)])
self.assertEqual(supplier_orders.dest_address_id, subcontractor01 | subcontractor02)
self.assertRecordValues(supplier_orders.order_line, [
{'product_id': component.id, 'product_qty': 1.0},
{'product_id': component.id, 'product_qty': 1.0},
])
def test_subcontracted_bom_routes(self):
"""
Take two BoM having those components. One being subcontracted and the other not.
- Compo RR : Buy & Reordering rule to resupply subcontractor.
- Compo DROP : Buy & Dropship subcontractor on order.
- Compo DROP : Buy & Dropship.
Check that depending on the context, the right route is shown on the report.
"""
route_buy = self.env.ref('purchase_stock.route_warehouse0_buy')
route_dropship = self.env['stock.route'].search([('name', '=', 'Dropship Subcontractor on Order')], limit=1)
dropship_route = self.env['stock.route'].search([('name', '=', 'Dropship')], limit=1)
warehouse = self.env['stock.warehouse'].search([], limit=1)
warehouse.manufacture_pull_id.route_id.write({'sequence': 20})
compo_drop, compo_rr = self.env['product.product'].create([{
'name': name,
'type': 'product',
'is_storable': True,
'seller_ids': [Command.create({'partner_id': self.subcontractor_partner1.parent_id.id})],
'route_ids': [Command.set(routes)],
} for name, routes in [
('Compo DROP', [route_buy.id, route_dropship.id]),
('Compo DROP', [route_buy.id, dropship_route.id]),
('Compo RR', [route_buy.id]),
]])
@ -449,14 +311,14 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
# Need to add the subcontractor as Vendor to have the bom read as subcontracted.
self.comp1.write({'seller_ids': [Command.create({'partner_id': self.subcontractor_partner1.id})]})
report = self.env['report.mrp.report_bom_structure'].with_context(warehouse=warehouse.id)._get_report_data(bom_subcontract.id)
report = self.env['report.mrp.report_bom_structure'].with_context(warehouse_id=warehouse.id)._get_report_data(bom_subcontract.id)
component_lines = report.get('lines', []).get('components', [])
self.assertEqual(component_lines[0]['product_id'], compo_drop.id)
self.assertEqual(component_lines[0]['route_name'], 'Dropship Subcontractor on Order')
self.assertEqual(component_lines[0]['route_name'], 'Dropship')
self.assertEqual(component_lines[1]['product_id'], compo_rr.id)
self.assertEqual(component_lines[1]['route_name'], 'Buy', 'Despite the RR linked to it, it should still display the Buy route')
report = self.env['report.mrp.report_bom_structure'].with_context(warehouse=warehouse.id)._get_report_data(bom_local.id)
report = self.env['report.mrp.report_bom_structure'].with_context(warehouse_id=warehouse.id)._get_report_data(bom_local.id)
component_lines = report.get('lines', []).get('components', [])
self.assertEqual(component_lines[0]['product_id'], compo_drop.id)
self.assertEqual(component_lines[0]['route_name'], 'Buy', 'Outside of the subcontracted context, it should try to resupply stock.')
@ -464,7 +326,7 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(component_lines[1]['route_name'], 'Buy')
def test_partner_id_no_overwrite(self):
subcontract_location = self.env.company.subcontracting_location_id
subcontract_location = self.company.subcontracting_location_id
p1, p2 = self.env['res.partner'].create([
{'name': 'partner 1', 'property_stock_subcontractor': subcontract_location.id},
{'name': 'partner 2', 'property_stock_subcontractor': subcontract_location.id},
@ -473,12 +335,12 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
'name': 'Resupply Subcontractor',
'rule_ids': [(0, False, {
'name': 'Stock -> Subcontractor',
'location_src_id': self.env.ref('stock.stock_location_stock').id,
'location_src_id': self.stock_location.id,
'location_dest_id': subcontract_location.id,
'company_id': self.env.company.id,
'company_id': self.company.id,
'action': 'pull',
'auto': 'manual',
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'picking_type_id': self.warehouse.out_type_id.id,
'partner_address_id': p1.id,
})],
})
@ -487,9 +349,126 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
'location_id': subcontract_location.id,
'route_id': route_resupply.id,
'product_id': self.comp1.id,
'warehouse_id': self.warehouse.id,
'product_min_qty': 2,
'product_max_qty': 2,
})
self.env['procurement.group'].run_scheduler()
self.env['stock.rule'].run_scheduler()
delivery = self.env["stock.move"].search([("product_id", "=", self.comp1.id)]).picking_id
self.assertEqual(delivery.partner_id, p1)
def test_shared_purchase_from_so(self):
customer = self.env['res.partner'].create([
{'name': 'customer'},
])
vendor = self.env['res.partner'].create([
{'name': 'vendor'},
])
route_mto = self.env.ref('stock.route_warehouse0_mto')
route_mto.active = True
component = self.env['product.product'].create([{
'name': 'Common Component',
'is_storable': True,
'route_ids': [Command.set(route_mto.ids)],
'seller_ids': [Command.create({'partner_id': vendor.id})],
}])
finished_products = self.env['product.product']
boms = self.env['mrp.bom']
for finished_product_name in ['fp1', 'fp2']:
finished_product = self.env['product.product'].create([{
'name': finished_product_name,
'is_storable': True,
'route_ids': [Command.set(route_mto.ids)],
}])
bom_form = Form(self.env['mrp.bom'])
bom_form.product_tmpl_id = finished_product.product_tmpl_id
with bom_form.bom_line_ids.new() as bom_line:
bom_line.product_id = component
bom_line.product_qty = 1
bom = bom_form.save()
finished_products += finished_product
boms += bom
uom_unit = self.env.ref('uom.product_uom_unit')
so = self.env['sale.order'].create({
"partner_id": customer.id,
"order_line": [
Command.create({
'product_id': finished_products[0].id,
'name': finished_products[0].name,
'product_uom_qty': 2,
'product_uom_id': uom_unit.id,
}),
Command.create({
'product_id': finished_products[1].id,
'name': finished_products[1].name,
'product_uom_qty': 2,
'product_uom_id': uom_unit.id,
}),
],
})
so.action_confirm()
mo_1 = self.env['mrp.production'].search([('bom_id', '=', boms[0].id)], limit=1)
self.assertTrue(mo_1)
mo_2 = self.env['mrp.production'].search([('bom_id', '=', boms[1].id)], limit=1)
self.assertTrue(mo_2)
po_vendor = self.env['purchase.order'].search([('partner_id', '=', vendor.id)])
self.assertTrue(po_vendor)
self.assertEqual(len(po_vendor.order_line), 1)
self.assertEqual(sum(po_vendor.order_line.mapped('product_qty')), 4)
po_vendor.button_confirm()
self.assertEqual(len(po_vendor.order_line.move_ids), 1)
self.assertFalse(po_vendor.order_line.move_ids.production_group_id)
self.assertEqual(po_vendor.order_line.move_dest_ids.production_group_id, mo_1.production_group_id | mo_2.production_group_id)
def test_portal_subcontractor_record_production_with_dropship(self):
"""
Check that a portal subcontractor is able to set serial numbers for
the final product (with a dropshipped component).
"""
portal_user = self.env['res.users'].create({
'name': 'portal user (subcontractor)',
'partner_id': self.subcontractor_partner1.id,
'login': 'subcontractor',
'password': 'subcontractor',
'email': 'subcontractor@subcontracting.portal',
'group_ids': [Command.set([self.env.ref('base.group_portal').id, self.env.ref('stock.group_production_lot').id])]
})
self.finished.tracking = 'serial'
self.bom.bom_line_ids[1].unlink()
dropship_routes = self.env.ref('stock_dropshipping.route_drop_shipping')
self.comp1.route_ids = [Command.link(dropship_routes.id)]
self.comp1.seller_ids = [Command.create({'partner_id': self.vendor.id})]
finished_serial = self.env['stock.lot'].create({
'name': 'SN404',
'product_id': self.finished.id,
})
po = self.env['purchase.order'].create({
"partner_id": self.subcontractor_partner1.id,
"dest_address_id": self.subcontractor_partner1.id,
"order_line": [Command.create({
'product_id': self.finished.id,
'name': self.finished.name,
'product_qty': 2,
})],
})
po.button_confirm()
subcontracted_mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)], limit=1)
# confirm the po to resuply the subcontractor
po_dropship_subcontractor = self.env['purchase.order'].search([('partner_id', '=', self.vendor.id)], limit=1)
po_dropship_subcontractor.button_confirm()
# check that the dropship is linked to the subcontracted MO
self.assertEqual(po_dropship_subcontractor.picking_ids.picking_type_id, self.company.dropship_subcontractor_pick_type_id)
self.assertEqual(po_dropship_subcontractor.picking_ids, subcontracted_mo.picking_ids)
# check that your subcontractor is able to modify the lot of the finished product
action = subcontracted_mo.incoming_picking.with_user(portal_user).with_context(is_subcontracting_portal=True).move_ids.action_show_details()
move = po.picking_ids[0].move_ids.with_user(portal_user)
move_form = Form(move.with_context(action['context']), view=action['view_id'])
# Registering components for the first manufactured product
with move_form.move_line_ids.edit(0) as ml:
ml.lot_id = finished_serial
move_form.save()
self.assertRecordValues(move._get_subcontract_production()[1], [{
'qty_producing': 0.0, 'lot_producing_ids': finished_serial.ids, 'state': 'confirmed',
}])

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import Form
@ -41,7 +40,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
],
})
sale_order = self.env['sale.order'].create({
sale_order = self.env['sale.order'].sudo().create({
'partner_id': self.customer.id,
'picking_policy': 'direct',
'order_line': [
@ -57,16 +56,12 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
# Deliver the first one
picking = sale_order.picking_ids.filtered(lambda p: p.partner_id == partners[0])
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0)
# Deliver the third one
picking = sale_order.picking_ids.filtered(lambda p: p.partner_id == partners[2])
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0)
# Cancel the second one
@ -94,7 +89,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
],
})
sale_order = self.env['sale.order'].create({
sale_order = self.env['sale.order'].sudo().create({
'partner_id': self.customer.id,
'picking_policy': 'direct',
'order_line': [
@ -102,27 +97,25 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
],
})
sale_order.action_confirm()
self.env['purchase.order'].search([], order='id desc', limit=1).button_confirm()
sale_order._get_purchase_orders().button_confirm()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0)
picking = sale_order.picking_ids
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 1.0)
for case in ['return', 'deliver again']:
delivered_before_case = 1.0 if case == 'return' else 0.0
delivered_after_case = 0.0 if case == 'return' else 1.0
return_form = Form(self.env['stock.return.picking'].with_context(active_ids=[picking.id], active_id=picking.id, active_model='stock.picking'))
with return_form.product_return_moves.edit(0) as line_form:
line_form.quantity = 1.0
return_wizard = return_form.save()
action = return_wizard.create_returns()
action = return_wizard.action_create_returns()
picking = self.env['stock.picking'].browse(action['res_id'])
self.assertEqual(sale_order.order_line.qty_delivered, delivered_before_case, "Incorrect delivered qty for case '%s'" % case)
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, delivered_after_case, "Incorrect delivered qty for case '%s'" % case)
def test_partial_return_kit_and_delivered_qty(self):
@ -154,7 +147,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
],
})
sale_order = self.env['sale.order'].create({
sale_order = self.env['sale.order'].sudo().create({
'partner_id': self.customer.id,
'picking_policy': 'direct',
'order_line': [
@ -162,36 +155,36 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
],
})
sale_order.action_confirm()
self.env['purchase.order'].search([], order='id desc', limit=1).button_confirm()
sale_order._get_purchase_orders().button_confirm()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 0/4")
picking01 = sale_order.picking_ids
picking01.move_ids.quantity_done = 2
action = picking01.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking01.move_ids.quantity = 2
picking01.move_ids.picked = True
Form.from_action(self.env, picking01.button_validate()).save().process()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 2/4")
# Create a return of picking01 (with both components)
return_form = Form(self.env['stock.return.picking'].with_context(active_id=picking01.id, active_model='stock.picking'))
wizard = return_form.save()
wizard.product_return_moves.write({'quantity': 2.0})
res = wizard.create_returns()
res = wizard.action_create_returns()
return01 = self.env['stock.picking'].browse(res['res_id'])
return01.move_ids.quantity_done = 2
return01.move_ids.quantity = 2
return01.move_ids.picked = True
return01.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 0/4")
picking02 = picking01.backorder_ids
picking02.move_ids.quantity_done = 1
action = picking02.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking02.move_ids.quantity = 1
picking02.move_ids.picked = True
Form.from_action(self.env, picking02.button_validate()).save().process()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 1/4")
picking03 = picking02.backorder_ids
picking03.move_ids.quantity_done = 1
picking03.move_ids.quantity = 1
picking03.move_ids.picked = True
picking03.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 2/4")
@ -199,10 +192,11 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
return_form = Form(self.env['stock.return.picking'].with_context(active_id=return01.id, active_model='stock.picking'))
wizard = return_form.save()
wizard.product_return_moves.write({'quantity': 1.0})
res = wizard.create_returns()
res = wizard.action_create_returns()
picking04 = self.env['stock.picking'].browse(res['res_id'])
picking04.move_ids.quantity_done = 1
picking04.move_ids.quantity = 1
picking04.move_ids.picked = True
picking04.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 3/4")
@ -210,10 +204,11 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
return_form = Form(self.env['stock.return.picking'].with_context(active_id=return01.id, active_model='stock.picking'))
wizard = return_form.save()
wizard.product_return_moves.write({'quantity': 1.0})
res = wizard.create_returns()
res = wizard.action_create_returns()
picking04 = self.env['stock.picking'].browse(res['res_id'])
picking04.move_ids.quantity_done = 1
picking04.move_ids.quantity = 1
picking04.move_ids.picked = True
picking04.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 1, "Delivered components: 4/4")
@ -237,7 +232,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
],
})
sale_order = self.env['sale.order'].create({
sale_order = self.env['sale.order'].sudo().create({
'partner_id': self.customer.id,
'picking_policy': 'direct',
'order_line': [
@ -276,7 +271,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
],
})
sale_order = self.env['sale.order'].create({
sale_order = self.env['sale.order'].sudo().create({
'partner_id': self.customer.id,
'picking_policy': 'direct',
'order_line': [
@ -286,8 +281,90 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
sale_order.action_confirm()
self.env['purchase.order'].search([], order='id desc', limit=1).button_confirm()
sale_order.picking_ids.move_ids.quantity_done = 1
sale_order.picking_ids.move_ids.quantity = 1
sale_order.picking_ids.move_ids.picked = True
sale_order.picking_ids[0].button_validate()
sale_order.picking_ids[1].button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 1.0)
def test_kit_dropshipped_change_qty_SO(self):
# Create BoM
product_a, product_b, final_product = self.env['product.product'].create([{
'name': p_name,
'type': 'consu',
'is_storable': True,
'seller_ids': [(0, 0, {
'partner_id': self.supplier.id,
})],
} for p_name in ['Comp 1', 'Comp 2', 'Final Product']])
product_a.route_ids = self.env.ref('stock_dropshipping.route_drop_shipping')
product_b.route_ids = self.env.ref('stock_dropshipping.route_drop_shipping')
self.env['mrp.bom'].create({
'product_id': final_product.id,
'product_tmpl_id': final_product.product_tmpl_id.id,
'product_qty': 1,
'consumption': 'flexible',
'type': 'phantom',
'bom_line_ids': [
(0, 0, {'product_id': product_a.id, 'product_qty': 1}),
(0, 0, {'product_id': product_b.id, 'product_qty': 1}),
]
})
# Create sale order
partner = self.env['res.partner'].create({
'name': 'Testing Man',
'email': 'another@user.com',
})
so = self.env['sale.order'].create({
'partner_id': partner.id,
})
sol = self.env['sale.order.line'].create({
'name': "Order line",
'product_id': final_product.id,
'order_id': so.id,
'product_uom_qty': 25,
})
so.action_confirm()
sol.write({'product_uom_qty': 10})
self.assertEqual(sol.purchase_line_ids.mapped('product_uom_qty'), [10, 10])
def test_dropship_move_lines_have_bom_line_id(self):
""" When selling a dropshipped kit, ensure that the move lines have a correctly
assigned bom_line_id
"""
compo, kit = self.env['product.product'].create([{
'name': n,
'type': 'consu',
'route_ids': [(6, 0, [self.dropship_route.id])],
'seller_ids': [(0, 0, {'partner_id': self.supplier.id})],
} for n in ['Compo', 'Kit']])
kit_bom = self.env['mrp.bom'].create({
'product_tmpl_id': kit.product_tmpl_id.id,
'product_qty': 1,
'type': 'phantom',
'bom_line_ids': [
(0, 0, {'product_id': compo.id, 'product_qty': 1}),
],
})
sale_order = self.env['sale.order'].create({
'partner_id': self.customer.id,
'picking_policy': 'direct',
'order_line': [
(0, 0, {'name': kit.name, 'product_id': kit.id, 'product_uom_qty': 1}),
],
})
sale_order.action_confirm()
sale_order._get_purchase_orders().button_confirm()
picking = sale_order.picking_ids
picking.button_validate()
purchase_order = sale_order._get_purchase_orders()
purchase_order.button_confirm()
# The bom_line_id on the stock move should be set
compo_move = sale_order.order_line.move_ids.filtered(lambda sm: sm.product_id == compo)
compo_bom_line = kit_bom.bom_line_ids.filtered(lambda bl: bl.product_id == compo)
self.assertTrue(compo_move.bom_line_id == compo_bom_line, "The bom_line_id on the stock move was set incorrectly")