chore: Stock Entry Tests and fixes

- Maintain item-warehouse wise rules for Stock Entry Material Transfer
- Test cases for Stock Entry with more use cases
- Sider fixes
This commit is contained in:
marination 2020-12-14 11:48:04 +05:30
parent a5d8d32775
commit c47d38dc18
6 changed files with 216 additions and 70 deletions

View File

@ -2030,7 +2030,7 @@ erpnext.show_serial_batch_selector = function (frm, d, callback, on_close, show_
}); });
} }
erpnext.apply_putaway_rule = (frm) => { erpnext.apply_putaway_rule = (frm, purpose=null) => {
if (!frm.doc.company) { if (!frm.doc.company) {
frappe.throw({message: __("Please select a Company first."), title: __("Mandatory")}); frappe.throw({message: __("Please select a Company first."), title: __("Mandatory")});
} }
@ -2042,7 +2042,8 @@ erpnext.apply_putaway_rule = (frm) => {
doctype: frm.doctype, doctype: frm.doctype,
items: frm.doc.items, items: frm.doc.items,
company: frm.doc.company, company: frm.doc.company,
sync: true sync: true,
purpose: purpose
}, },
callback: (result) => { callback: (result) => {
if (!result.exc && result.message) { if (!result.exc && result.message) {

View File

@ -64,11 +64,14 @@ def get_putaway_capacity(rule):
return free_space if free_space > 0 else 0 return free_space if free_space > 0 else 0
@frappe.whitelist() @frappe.whitelist()
def apply_putaway_rule(doctype, items, company, sync=None): def apply_putaway_rule(doctype, items, company, sync=None, purpose=None):
""" Applies Putaway Rule on line items. """ Applies Putaway Rule on line items.
items: List of Purchase Receipt Item objects items: List of Purchase Receipt/Stock Entry Items
company: Company in the Purchase Receipt company: Company in the Purchase Receipt/Stock Entry
doctype: Doctype to apply rule on
purpose: Purpose of Stock Entry
sync (optional): Sync with client side only for client side calls
""" """
if isinstance(items, string_types): if isinstance(items, string_types):
items = json.loads(items) items = json.loads(items)
@ -82,31 +85,36 @@ def apply_putaway_rule(doctype, items, company, sync=None):
source_warehouse = item.get("s_warehouse") source_warehouse = item.get("s_warehouse")
serial_nos = get_serial_nos(item.get("serial_no")) serial_nos = get_serial_nos(item.get("serial_no"))
item.conversion_factor = flt(item.conversion_factor) or 1 item.conversion_factor = flt(item.conversion_factor) or 1.0
pending_qty, item_code = flt(item.qty), item.item_code pending_qty, item_code = flt(item.qty), item.item_code
pending_stock_qty = flt(item.transfer_qty) if doctype == "Stock Entry" else flt(item.stock_qty) pending_stock_qty = flt(item.transfer_qty) if doctype == "Stock Entry" else flt(item.stock_qty)
if not pending_qty or not item_code:
updated_table = add_row(item, pending_qty, item.warehouse, updated_table)
continue
uom_must_be_whole_number = frappe.db.get_value('UOM', item.uom, 'must_be_whole_number') uom_must_be_whole_number = frappe.db.get_value('UOM', item.uom, 'must_be_whole_number')
if not pending_qty or not item_code:
updated_table = add_row(item, pending_qty, source_warehouse or item.warehouse, updated_table)
continue
at_capacity, rules = get_ordered_putaway_rules(item_code, company, source_warehouse=source_warehouse) at_capacity, rules = get_ordered_putaway_rules(item_code, company, source_warehouse=source_warehouse)
if not rules: if not rules:
warehouse = item.warehouse warehouse = source_warehouse or item.warehouse
if at_capacity: if at_capacity:
warehouse = '' # rules available, but no free space # rules available, but no free space
items_not_accomodated.append([item_code, pending_qty]) items_not_accomodated.append([item_code, pending_qty])
updated_table = add_row(item, pending_qty, warehouse, updated_table) else:
updated_table = add_row(item, pending_qty, warehouse, updated_table)
continue continue
# maintain item wise rules, to handle if item is entered twice # maintain item/item-warehouse wise rules, to handle if item is entered twice
# in the table, due to different price, etc. # in the table, due to different price, etc.
if not item_wise_rules[item_code]: key = item_code
item_wise_rules[item_code] = rules if doctype == "Stock Entry" and purpose == "Material Transfer" and source_warehouse:
key = (item_code, source_warehouse)
for rule in item_wise_rules[item_code]: if not item_wise_rules[key]:
item_wise_rules[key] = rules
for rule in item_wise_rules[key]:
if pending_stock_qty > 0 and rule.free_space: if pending_stock_qty > 0 and rule.free_space:
stock_qty_to_allocate = flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty stock_qty_to_allocate = flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty
qty_to_allocate = stock_qty_to_allocate / item.conversion_factor qty_to_allocate = stock_qty_to_allocate / item.conversion_factor

View File

@ -4,12 +4,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
from frappe.utils import add_days, nowdate
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.stock.doctype.batch.test_batch import make_new_batch
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
class TestPutawayRule(unittest.TestCase): class TestPutawayRule(unittest.TestCase):
@ -27,6 +26,9 @@ class TestPutawayRule(unittest.TestCase):
if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 2"}): if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 2"}):
create_warehouse("Rack 2") create_warehouse("Rack 2")
self.warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"})
self.warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
if not frappe.db.exists("UOM", "Bag"): if not frappe.db.exists("UOM", "Bag"):
new_uom = frappe.new_doc("UOM") new_uom = frappe.new_doc("UOM")
new_uom.uom_name = "Bag" new_uom.uom_name = "Bag"
@ -34,21 +36,18 @@ class TestPutawayRule(unittest.TestCase):
def test_putaway_rules_priority(self): def test_putaway_rules_priority(self):
"""Test if rule is applied by priority, irrespective of free space.""" """Test if rule is applied by priority, irrespective of free space."""
warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=200,
uom="Kg") uom="Kg")
rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=300, rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=300,
uom="Kg", priority=2) uom="Kg", priority=2)
pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1, pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1,
do_not_submit=1) do_not_submit=1)
self.assertEqual(len(pr.items), 2) self.assertEqual(len(pr.items), 2)
self.assertEqual(pr.items[0].qty, 200) self.assertEqual(pr.items[0].qty, 200)
self.assertEqual(pr.items[0].warehouse, warehouse_1) self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
self.assertEqual(pr.items[1].qty, 100) self.assertEqual(pr.items[1].qty, 100)
self.assertEqual(pr.items[1].warehouse, warehouse_2) self.assertEqual(pr.items[1].warehouse, self.warehouse_2)
pr.delete() pr.delete()
rule_1.delete() rule_1.delete()
@ -57,26 +56,23 @@ class TestPutawayRule(unittest.TestCase):
def test_putaway_rules_with_same_priority(self): def test_putaway_rules_with_same_priority(self):
"""Test if rule with more free space is applied, """Test if rule with more free space is applied,
among two rules with same priority and capacity.""" among two rules with same priority and capacity."""
warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=500,
warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=500,
uom="Kg") uom="Kg")
rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=500, rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500,
uom="Kg") uom="Kg")
# out of 500 kg capacity, occupy 100 kg in warehouse_1 # out of 500 kg capacity, occupy 100 kg in warehouse_1
stock_receipt = make_stock_entry(item_code="_Rice", target=warehouse_1, qty=100, basic_rate=50) stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=100, basic_rate=50)
pr = make_purchase_receipt(item_code="_Rice", qty=700, apply_putaway_rule=1, pr = make_purchase_receipt(item_code="_Rice", qty=700, apply_putaway_rule=1,
do_not_submit=1) do_not_submit=1)
self.assertEqual(len(pr.items), 2) self.assertEqual(len(pr.items), 2)
self.assertEqual(pr.items[0].qty, 500) self.assertEqual(pr.items[0].qty, 500)
# warehouse_2 has 500 kg free space, it is given priority # warehouse_2 has 500 kg free space, it is given priority
self.assertEqual(pr.items[0].warehouse, warehouse_2) self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
self.assertEqual(pr.items[1].qty, 200) self.assertEqual(pr.items[1].qty, 200)
# warehouse_1 has 400 kg free space, it is given less priority # warehouse_1 has 400 kg free space, it is given less priority
self.assertEqual(pr.items[1].warehouse, warehouse_1) self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
stock_receipt.cancel() stock_receipt.cancel()
pr.delete() pr.delete()
@ -85,21 +81,20 @@ class TestPutawayRule(unittest.TestCase):
def test_putaway_rules_with_insufficient_capacity(self): def test_putaway_rules_with_insufficient_capacity(self):
"""Test if qty exceeding capacity, is handled.""" """Test if qty exceeding capacity, is handled."""
warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=100,
warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=100,
uom="Kg") uom="Kg")
rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=200, rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=200,
uom="Kg") uom="Kg")
pr = make_purchase_receipt(item_code="_Rice", qty=350, apply_putaway_rule=1, pr = make_purchase_receipt(item_code="_Rice", qty=350, apply_putaway_rule=1,
do_not_submit=1) do_not_submit=1)
self.assertEqual(len(pr.items), 2) self.assertEqual(len(pr.items), 2)
self.assertEqual(pr.items[0].qty, 200) self.assertEqual(pr.items[0].qty, 200)
self.assertEqual(pr.items[0].warehouse, warehouse_2) self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
self.assertEqual(pr.items[1].qty, 100) self.assertEqual(pr.items[1].qty, 100)
self.assertEqual(pr.items[1].warehouse, warehouse_1) self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
# total 300 assigned, 50 unassigned
pr.delete() pr.delete()
rule_1.delete() rule_1.delete()
rule_2.delete() rule_2.delete()
@ -114,26 +109,23 @@ class TestPutawayRule(unittest.TestCase):
}) })
item.save() item.save()
warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=3,
warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=3,
uom="Bag") uom="Bag")
self.assertEqual(rule_1.stock_capacity, 3000) self.assertEqual(rule_1.stock_capacity, 3000)
rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=4, rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=4,
uom="Bag") uom="Bag")
self.assertEqual(rule_2.stock_capacity, 4000) self.assertEqual(rule_2.stock_capacity, 4000)
# populate 'Rack 1' with 1 Bag, making the free space 2 Bags # populate 'Rack 1' with 1 Bag, making the free space 2 Bags
stock_receipt = make_stock_entry(item_code="_Rice", target=warehouse_1, qty=1000, basic_rate=50) stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=1000, basic_rate=50)
pr = make_purchase_receipt(item_code="_Rice", qty=6, uom="Bag", stock_uom="Kg", pr = make_purchase_receipt(item_code="_Rice", qty=6, uom="Bag", stock_uom="Kg",
conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1) conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1)
self.assertEqual(len(pr.items), 2) self.assertEqual(len(pr.items), 2)
self.assertEqual(pr.items[0].qty, 4) self.assertEqual(pr.items[0].qty, 4)
self.assertEqual(pr.items[0].warehouse, warehouse_2) self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
self.assertEqual(pr.items[1].qty, 2) self.assertEqual(pr.items[1].qty, 2)
self.assertEqual(pr.items[1].warehouse, warehouse_1) self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
stock_receipt.cancel() stock_receipt.cancel()
pr.delete() pr.delete()
@ -152,15 +144,12 @@ class TestPutawayRule(unittest.TestCase):
frappe.db.set_value("UOM", "Bag", "must_be_whole_number", 1) frappe.db.set_value("UOM", "Bag", "must_be_whole_number", 1)
warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"})
warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
# Putaway Rule in different UOM # Putaway Rule in different UOM
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=1, rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=1,
uom="Bag") uom="Bag")
self.assertEqual(rule_1.stock_capacity, 1000) self.assertEqual(rule_1.stock_capacity, 1000)
# Putaway Rule in Stock UOM # Putaway Rule in Stock UOM
rule_2 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_2, capacity=500) rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500)
self.assertEqual(rule_2.stock_capacity, 500) self.assertEqual(rule_2.stock_capacity, 500)
# total capacity is 1500 Kg # total capacity is 1500 Kg
@ -168,7 +157,7 @@ class TestPutawayRule(unittest.TestCase):
conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1) conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1)
self.assertEqual(len(pr.items), 1) self.assertEqual(len(pr.items), 1)
self.assertEqual(pr.items[0].qty, 1) self.assertEqual(pr.items[0].qty, 1)
self.assertEqual(pr.items[0].warehouse, warehouse_1) self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
# leftover space was for 500 kg (0.5 Bag) # leftover space was for 500 kg (0.5 Bag)
# Since Bag is a whole UOM, 1(out of 2) Bag will be unassigned # Since Bag is a whole UOM, 1(out of 2) Bag will be unassigned
@ -177,11 +166,8 @@ class TestPutawayRule(unittest.TestCase):
rule_2.delete() rule_2.delete()
def test_putaway_rules_with_reoccurring_item(self): def test_putaway_rules_with_reoccurring_item(self):
"""Test rules on same item entered multiple times.""" """Test rules on same item entered multiple times with different rate."""
warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=200,
uom="Kg") uom="Kg")
# total capacity is 200 Kg # total capacity is 200 Kg
@ -201,12 +187,12 @@ class TestPutawayRule(unittest.TestCase):
pr.save() pr.save()
self.assertEqual(len(pr.items), 2) self.assertEqual(len(pr.items), 2)
self.assertEqual(pr.items[0].qty, 100) self.assertEqual(pr.items[0].qty, 100)
self.assertEqual(pr.items[0].warehouse, warehouse_1) self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
self.assertEqual(pr.items[0].putaway_rule, rule_1.name) self.assertEqual(pr.items[0].putaway_rule, rule_1.name)
# same rule applied to second item row # same rule applied to second item row
# with previous assignment considered # with previous assignment considered
self.assertEqual(pr.items[1].qty, 100) # 100 unassigned in second row from 200 self.assertEqual(pr.items[1].qty, 100) # 100 unassigned in second row from 200
self.assertEqual(pr.items[1].warehouse, warehouse_1) self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
self.assertEqual(pr.items[1].putaway_rule, rule_1.name) self.assertEqual(pr.items[1].putaway_rule, rule_1.name)
pr.delete() pr.delete()
@ -214,17 +200,14 @@ class TestPutawayRule(unittest.TestCase):
def test_validate_over_receipt_in_warehouse(self): def test_validate_over_receipt_in_warehouse(self):
"""Test if overreceipt is blocked in the presence of putaway rules.""" """Test if overreceipt is blocked in the presence of putaway rules."""
warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"}) rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=warehouse_1, capacity=200,
uom="Kg") uom="Kg")
pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1, pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1,
do_not_submit=1) do_not_submit=1)
self.assertEqual(len(pr.items), 1) self.assertEqual(len(pr.items), 1)
self.assertEqual(pr.items[0].qty, 200) # 100 is unassigned fro 300 Kg self.assertEqual(pr.items[0].qty, 200) # 100 is unassigned fro 300 Kg
self.assertEqual(pr.items[0].warehouse, warehouse_1) self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
self.assertEqual(pr.items[0].putaway_rule, rule_1.name) self.assertEqual(pr.items[0].putaway_rule, rule_1.name)
# force overreceipt and disable apply putaway rule in PR # force overreceipt and disable apply putaway rule in PR
@ -236,6 +219,156 @@ class TestPutawayRule(unittest.TestCase):
pr.delete() pr.delete()
rule_1.delete() rule_1.delete()
def test_putaway_rule_on_stock_entry_material_transfer(self):
"""Test if source warehouse is considered while applying rules."""
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
uom="Kg") # higher priority
rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100,
uom="Kg", priority=2)
stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_1, qty=200,
target="_Test Warehouse - _TC", purpose="Material Transfer",
apply_putaway_rule=1, do_not_submit=1)
stock_entry_item = stock_entry.get("items")[0]
# since source warehouse is Rack 1, rule 1 (for Rack 1) will be avoided
# even though it has more free space and higher priority
self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_2)
self.assertEqual(stock_entry_item.qty, 100) # unassigned 100 out of 200 Kg
self.assertEqual(stock_entry_item.putaway_rule, rule_2.name)
stock_entry.delete()
rule_1.delete()
rule_2.delete()
def test_putaway_rule_on_stock_entry_material_transfer_reoccuring_item(self):
"""Test if reoccuring item is correctly considered."""
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=300,
uom="Kg")
rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=600,
uom="Kg", priority=2)
# create SE with first row having source warehouse as Rack 2
stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_2, qty=200,
target="_Test Warehouse - _TC", purpose="Material Transfer",
apply_putaway_rule=1, do_not_submit=1)
# Add rows with source warehouse as Rack 1
stock_entry.extend("items", [
{
"item_code": "_Rice",
"s_warehouse": self.warehouse_1,
"t_warehouse": "_Test Warehouse - _TC",
"qty": 100,
"basic_rate": 50,
"conversion_factor": 1.0,
"transfer_qty": 100
},
{
"item_code": "_Rice",
"s_warehouse": self.warehouse_1,
"t_warehouse": "_Test Warehouse - _TC",
"qty": 200,
"basic_rate": 60,
"conversion_factor": 1.0,
"transfer_qty": 200
}
])
stock_entry.save()
# since source warehouse was Rack 2, exclude rule_2
self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1)
self.assertEqual(stock_entry.items[0].qty, 200)
self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name)
# since source warehouse was Rack 1, exclude rule_1 even though it has
# higher priority
self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2)
self.assertEqual(stock_entry.items[1].qty, 100)
self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name)
self.assertEqual(stock_entry.items[2].t_warehouse, self.warehouse_2)
self.assertEqual(stock_entry.items[2].qty, 200)
self.assertEqual(stock_entry.items[2].putaway_rule, rule_2.name)
stock_entry.delete()
rule_1.delete()
rule_2.delete()
def test_putaway_rule_on_stock_entry_material_transfer_batch_serial_item(self):
"""Test if batch and serial items are split correctly."""
if not frappe.db.exists("Item", "Water Bottle"):
make_item("Water Bottle", {
"is_stock_item": 1,
"has_batch_no" : 1,
"create_new_batch": 1,
"has_serial_no": 1,
"serial_no_series": "BOTTL-.####",
"stock_uom": "Nos"
})
rule_1 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_1, capacity=3,
uom="Nos")
rule_2 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_2, capacity=2,
uom="Nos")
make_new_batch(batch_id="BOTTL-BATCH-1", item_code="Water Bottle")
pr = make_purchase_receipt(item_code="Water Bottle", qty=5, do_not_submit=1)
pr.items[0].batch_no = "BOTTL-BATCH-1"
pr.save()
pr.submit()
serial_nos = frappe.get_list("Serial No", filters={"purchase_document_no": pr.name, "status": "Active"})
serial_nos = [d.name for d in serial_nos]
stock_entry = make_stock_entry(item_code="Water Bottle", source="_Test Warehouse - _TC", qty=5,
target="Finished Goods - _TC", purpose="Material Transfer",
apply_putaway_rule=1, do_not_save=1)
stock_entry.items[0].batch_no = "BOTTL-BATCH-1"
stock_entry.items[0].serial_no = "\n".join(serial_nos)
stock_entry.save()
self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1)
self.assertEqual(stock_entry.items[0].qty, 3)
self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name)
self.assertEqual(stock_entry.items[0].serial_no, "\n".join(serial_nos[:3]))
self.assertEqual(stock_entry.items[0].batch_no, "BOTTL-BATCH-1")
self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2)
self.assertEqual(stock_entry.items[1].qty, 2)
self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name)
self.assertEqual(stock_entry.items[1].serial_no, "\n".join(serial_nos[3:]))
self.assertEqual(stock_entry.items[1].batch_no, "BOTTL-BATCH-1")
stock_entry.delete()
pr.cancel()
rule_1.delete()
rule_2.delete()
def test_putaway_rule_on_stock_entry_material_receipt(self):
"""Test if rules are applied in Stock Entry of type Receipt."""
rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
uom="Kg") # more capacity
rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100,
uom="Kg")
stock_entry = make_stock_entry(item_code="_Rice", qty=100,
target="_Test Warehouse - _TC", purpose="Material Receipt",
apply_putaway_rule=1, do_not_submit=1)
stock_entry_item = stock_entry.get("items")[0]
self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_1)
self.assertEqual(stock_entry_item.qty, 100)
self.assertEqual(stock_entry_item.putaway_rule, rule_1.name)
stock_entry.delete()
rule_1.delete()
rule_2.delete()
def create_putaway_rule(**args): def create_putaway_rule(**args):
args = frappe._dict(args) args = frappe._dict(args)
putaway = frappe.new_doc("Putaway Rule") putaway = frappe.new_doc("Putaway Rule")

View File

@ -574,7 +574,7 @@ frappe.ui.form.on('Stock Entry', {
}, },
apply_putaway_rule: function (frm) { apply_putaway_rule: function (frm) {
if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm); if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm, frm.doc.purpose);
} }
}); });

View File

@ -47,7 +47,8 @@ class StockEntry(StockController):
apply_rule = self.apply_putaway_rule and (self.purpose in ["Material Transfer", "Material Receipt"]) apply_rule = self.apply_putaway_rule and (self.purpose in ["Material Transfer", "Material Receipt"])
if self.get("items") and apply_rule: if self.get("items") and apply_rule:
apply_putaway_rule(self.doctype, self.get("items"), self.company) apply_putaway_rule(self.doctype, self.get("items"), self.company,
purpose=self.purpose)
def validate(self): def validate(self):
self.pro_doc = frappe._dict() self.pro_doc = frappe._dict()

View File

@ -53,6 +53,8 @@ def make_stock_entry(**args):
args.target = args.to_warehouse args.target = args.to_warehouse
if args.item_code: if args.item_code:
args.item = args.item_code args.item = args.item_code
if args.apply_putaway_rule:
s.apply_putaway_rule = args.apply_putaway_rule
if isinstance(args.qty, string_types): if isinstance(args.qty, string_types):
if '.' in args.qty: if '.' in args.qty:
@ -118,7 +120,8 @@ def make_stock_entry(**args):
"t_warehouse": args.target, "t_warehouse": args.target,
"qty": args.qty, "qty": args.qty,
"basic_rate": args.rate or args.basic_rate, "basic_rate": args.rate or args.basic_rate,
"conversion_factor": 1.0, "conversion_factor": args.conversion_factor or 1.0,
"transfer_qty": flt(args.qty) * (flt(args.conversion_factor) or 1.0),
"serial_no": args.serial_no, "serial_no": args.serial_no,
'batch_no': args.batch_no, 'batch_no': args.batch_no,
'cost_center': args.cost_center, 'cost_center': args.cost_center,