mirror of
https://github.com/bringout/oca-ocb-mrp.git
synced 2026-04-27 03:11:59 +02:00
19.0 vanilla
This commit is contained in:
parent
accf5918df
commit
6e65e8c877
688 changed files with 225434 additions and 199401 deletions
|
|
@ -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},
|
||||
])
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
}])
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue