blob: bf12181c527f30911cbaf922ffbc0fc33d2f857c [file] [log] [blame]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301import copy
Chillar Anand915b3432021-09-02 16:44:59 +05302import unittest
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05303from collections import defaultdict
Chillar Anand915b3432021-09-02 16:44:59 +05304
5import frappe
6from frappe.utils import cint
7
8from erpnext.buying.doctype.purchase_order.purchase_order import (
9 get_materials_from_supplier,
10 make_purchase_invoice,
11 make_purchase_receipt,
12 make_rm_stock_entry,
13)
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053014from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
Chillar Anand915b3432021-09-02 16:44:59 +053015from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
16from erpnext.stock.doctype.item.test_item import make_item
17from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
18from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
19
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053020
21class TestSubcontracting(unittest.TestCase):
22 def setUp(self):
23 make_subcontract_items()
24 make_raw_materials()
25 make_bom_for_subcontracted_items()
26
27 def test_po_with_bom(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +053028 """
29 - Set backflush based on BOM
30 - Create subcontracted PO for the item Subcontracted Item SA1 and add same item two times.
31 - Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
32 - Create purchase receipt against the PO and check serial nos and batch no.
33 """
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053034
Ankush Menat494bd9e2022-03-28 18:52:46 +053035 set_backflush_based_on("BOM")
36 item_code = "Subcontracted Item SA1"
37 items = [
38 {"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 5, "rate": 100},
39 {"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 6, "rate": 100},
40 ]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053041
Ankush Menat494bd9e2022-03-28 18:52:46 +053042 rm_items = [
43 {"item_code": "Subcontracted SRM Item 1", "qty": 5},
44 {"item_code": "Subcontracted SRM Item 2", "qty": 5},
45 {"item_code": "Subcontracted SRM Item 3", "qty": 5},
46 {"item_code": "Subcontracted SRM Item 1", "qty": 6},
47 {"item_code": "Subcontracted SRM Item 2", "qty": 6},
48 {"item_code": "Subcontracted SRM Item 3", "qty": 6},
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053049 ]
50
51 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +053052 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +053053 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +053054 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053055
56 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +053057 d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053058
Ankush Menat494bd9e2022-03-28 18:52:46 +053059 make_stock_transfer_entry(
60 po_no=po.name,
61 main_item_code=item_code,
62 rm_items=rm_items,
63 itemwise_details=copy.deepcopy(itemwise_details),
64 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053065
66 pr1 = make_purchase_receipt(po.name)
67 pr1.submit()
68
69 for key, value in get_supplied_items(pr1).items():
70 transferred_detais = itemwise_details.get(key)
71
Ankush Menat494bd9e2022-03-28 18:52:46 +053072 for field in ["qty", "serial_no", "batch_no"]:
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053073 if value.get(field):
74 transfer, consumed = (transferred_detais.get(field), value.get(field))
Ankush Menat494bd9e2022-03-28 18:52:46 +053075 if field == "serial_no":
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053076 transfer, consumed = (sorted(transfer), sorted(consumed))
77
78 self.assertEqual(transfer, consumed)
79
80 def test_po_with_material_transfer(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +053081 """
82 - Set backflush based on Material Transfer
83 - Create subcontracted PO for the item Subcontracted Item SA1 and Subcontracted Item SA5.
84 - Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
85 - Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5.
86 - Create partial purchase receipt against the PO and check serial nos and batch no.
87 """
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +053088
Ankush Menat494bd9e2022-03-28 18:52:46 +053089 set_backflush_based_on("Material Transferred for Subcontract")
90 items = [
91 {
92 "warehouse": "_Test Warehouse - _TC",
93 "item_code": "Subcontracted Item SA1",
94 "qty": 5,
95 "rate": 100,
96 },
97 {
98 "warehouse": "_Test Warehouse - _TC",
99 "item_code": "Subcontracted Item SA5",
100 "qty": 6,
101 "rate": 100,
102 },
103 ]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530104
Ankush Menat494bd9e2022-03-28 18:52:46 +0530105 rm_items = [
106 {"item_code": "Subcontracted SRM Item 1", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
107 {"item_code": "Subcontracted SRM Item 2", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
108 {"item_code": "Subcontracted SRM Item 3", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
109 {"item_code": "Subcontracted SRM Item 5", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
110 {"item_code": "Subcontracted SRM Item 4", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530111 ]
112
113 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530114 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530115 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530116 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530117
118 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530119 d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530120
Ankush Menat494bd9e2022-03-28 18:52:46 +0530121 make_stock_transfer_entry(
122 po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
123 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530124
125 pr1 = make_purchase_receipt(po.name)
126 pr1.remove(pr1.items[1])
127 pr1.submit()
128
129 for key, value in get_supplied_items(pr1).items():
130 transferred_detais = itemwise_details.get(key)
131
Ankush Menat494bd9e2022-03-28 18:52:46 +0530132 for field in ["qty", "serial_no", "batch_no"]:
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530133 if value.get(field):
134 self.assertEqual(value.get(field), transferred_detais.get(field))
135
136 pr2 = make_purchase_receipt(po.name)
137 pr2.submit()
138
139 for key, value in get_supplied_items(pr2).items():
140 transferred_detais = itemwise_details.get(key)
141
Ankush Menat494bd9e2022-03-28 18:52:46 +0530142 for field in ["qty", "serial_no", "batch_no"]:
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530143 if value.get(field):
144 self.assertEqual(value.get(field), transferred_detais.get(field))
145
146 def test_subcontract_with_same_components_different_fg(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530147 """
148 - Set backflush based on Material Transfer
149 - Create subcontracted PO for the item Subcontracted Item SA2 and Subcontracted Item SA3.
150 - Transfer the components from Stores to Supplier warehouse with serial nos.
151 - Transfer extra qty of components for the item Subcontracted Item SA2.
152 - Create partial purchase receipt against the PO and check serial nos and batch no.
153 """
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530154
Ankush Menat494bd9e2022-03-28 18:52:46 +0530155 set_backflush_based_on("Material Transferred for Subcontract")
156 items = [
157 {
158 "warehouse": "_Test Warehouse - _TC",
159 "item_code": "Subcontracted Item SA2",
160 "qty": 5,
161 "rate": 100,
162 },
163 {
164 "warehouse": "_Test Warehouse - _TC",
165 "item_code": "Subcontracted Item SA3",
166 "qty": 6,
167 "rate": 100,
168 },
169 ]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530170
Ankush Menat494bd9e2022-03-28 18:52:46 +0530171 rm_items = [
172 {"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA2"},
173 {"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA3"},
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530174 ]
175
176 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530177 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530178 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530179 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530180
181 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530182 d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530183
Ankush Menat494bd9e2022-03-28 18:52:46 +0530184 make_stock_transfer_entry(
185 po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
186 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530187
188 pr1 = make_purchase_receipt(po.name)
189 pr1.items[0].qty = 3
190 pr1.remove(pr1.items[1])
191 pr1.submit()
192
193 for key, value in get_supplied_items(pr1).items():
194 transferred_detais = itemwise_details.get(key)
195 self.assertEqual(value.qty, 4)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530196 self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:4]))
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530197
198 pr2 = make_purchase_receipt(po.name)
199 pr2.items[0].qty = 2
200 pr2.remove(pr2.items[1])
201 pr2.submit()
202
203 for key, value in get_supplied_items(pr2).items():
204 transferred_detais = itemwise_details.get(key)
205
206 self.assertEqual(value.qty, 2)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530207 self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[4:6]))
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530208
209 pr3 = make_purchase_receipt(po.name)
210 pr3.submit()
211 for key, value in get_supplied_items(pr3).items():
212 transferred_detais = itemwise_details.get(key)
213
214 self.assertEqual(value.qty, 6)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530215 self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12]))
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530216
217 def test_return_non_consumed_materials(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530218 """
219 - Set backflush based on Material Transfer
220 - Create subcontracted PO for the item Subcontracted Item SA2.
221 - Transfer the components from Stores to Supplier warehouse with serial nos.
222 - Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
223 - Create purchase receipt for full qty against the PO and change the qty of raw material.
224 - After that return the non consumed material back to the store from supplier's warehouse.
225 """
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530226
Ankush Menat494bd9e2022-03-28 18:52:46 +0530227 set_backflush_based_on("Material Transferred for Subcontract")
228 items = [
229 {
230 "warehouse": "_Test Warehouse - _TC",
231 "item_code": "Subcontracted Item SA2",
232 "qty": 5,
233 "rate": 100,
234 }
235 ]
236 rm_items = [
237 {"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA2"}
238 ]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530239
240 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530241 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530242 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530243 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530244
245 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530246 d["po_detail"] = po.items[0].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530247
Ankush Menat494bd9e2022-03-28 18:52:46 +0530248 make_stock_transfer_entry(
249 po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
250 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530251
252 pr1 = make_purchase_receipt(po.name)
253 pr1.save()
254 pr1.supplied_items[0].consumed_qty = 5
Ankush Menat494bd9e2022-03-28 18:52:46 +0530255 pr1.supplied_items[0].serial_no = "\n".join(
256 sorted(itemwise_details.get("Subcontracted SRM Item 2").get("serial_no")[0:5])
257 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530258 pr1.submit()
259
260 for key, value in get_supplied_items(pr1).items():
261 transferred_detais = itemwise_details.get(key)
262 self.assertEqual(value.qty, 5)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530263 self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:5]))
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530264
265 po.load_from_db()
266 self.assertEqual(po.supplied_items[0].consumed_qty, 5)
267 doc = get_materials_from_supplier(po.name, [d.name for d in po.supplied_items])
268 self.assertEqual(doc.items[0].qty, 1)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530269 self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC")
270 self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC")
271 self.assertEqual(
272 get_serial_nos(doc.items[0].serial_no),
273 itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
274 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530275
276 def test_item_with_batch_based_on_bom(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530277 """
278 - Set backflush based on BOM
279 - Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
280 - Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
281 - Transfer the components in multiple batches.
282 - Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
283 - Keep the qty as 2 for Subcontracted Item in the purchase receipt.
284 """
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530285
Ankush Menat494bd9e2022-03-28 18:52:46 +0530286 set_backflush_based_on("BOM")
287 item_code = "Subcontracted Item SA4"
288 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530289
Ankush Menat494bd9e2022-03-28 18:52:46 +0530290 rm_items = [
291 {"item_code": "Subcontracted SRM Item 1", "qty": 10},
292 {"item_code": "Subcontracted SRM Item 2", "qty": 10},
293 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
294 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
295 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
296 {"item_code": "Subcontracted SRM Item 3", "qty": 1},
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530297 ]
298
299 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530300 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530301 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530302 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530303
304 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530305 d["po_detail"] = po.items[0].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530306
Ankush Menat494bd9e2022-03-28 18:52:46 +0530307 make_stock_transfer_entry(
308 po_no=po.name,
309 main_item_code=item_code,
310 rm_items=rm_items,
311 itemwise_details=copy.deepcopy(itemwise_details),
312 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530313
314 pr1 = make_purchase_receipt(po.name)
315 pr1.items[0].qty = 2
316 add_second_row_in_pr(pr1)
317 pr1.save()
318 pr1.submit()
319
320 for key, value in get_supplied_items(pr1).items():
321 self.assertEqual(value.qty, 4)
322
323 pr1 = make_purchase_receipt(po.name)
324 pr1.items[0].qty = 2
325 add_second_row_in_pr(pr1)
326 pr1.save()
327 pr1.submit()
328
329 for key, value in get_supplied_items(pr1).items():
330 self.assertEqual(value.qty, 4)
331
332 pr1 = make_purchase_receipt(po.name)
333 pr1.items[0].qty = 2
334 pr1.save()
335 pr1.submit()
336
337 for key, value in get_supplied_items(pr1).items():
338 self.assertEqual(value.qty, 2)
339
340 def test_item_with_batch_based_on_material_transfer(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530341 """
342 - Set backflush based on Material Transferred for Subcontract
343 - Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
344 - Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
345 - Transfer the components in multiple batches with extra 2 qty for the batched item.
346 - Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
347 - Keep the qty as 2 for Subcontracted Item in the purchase receipt.
348 - In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
349 """
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530350
Ankush Menat494bd9e2022-03-28 18:52:46 +0530351 set_backflush_based_on("Material Transferred for Subcontract")
352 item_code = "Subcontracted Item SA4"
353 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530354
Ankush Menat494bd9e2022-03-28 18:52:46 +0530355 rm_items = [
356 {"item_code": "Subcontracted SRM Item 1", "qty": 10},
357 {"item_code": "Subcontracted SRM Item 2", "qty": 10},
358 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
359 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
360 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
361 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530362 ]
363
364 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530365 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530366 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530367 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530368
369 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530370 d["po_detail"] = po.items[0].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530371
Ankush Menat494bd9e2022-03-28 18:52:46 +0530372 make_stock_transfer_entry(
373 po_no=po.name,
374 main_item_code=item_code,
375 rm_items=rm_items,
376 itemwise_details=copy.deepcopy(itemwise_details),
377 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530378
379 pr1 = make_purchase_receipt(po.name)
380 pr1.items[0].qty = 2
381 add_second_row_in_pr(pr1)
382 pr1.save()
383 pr1.submit()
384
385 for key, value in get_supplied_items(pr1).items():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530386 qty = 4 if key != "Subcontracted SRM Item 3" else 6
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530387 self.assertEqual(value.qty, qty)
388
389 pr1 = make_purchase_receipt(po.name)
390 pr1.items[0].qty = 2
391 add_second_row_in_pr(pr1)
392 pr1.save()
393 pr1.submit()
394
395 for key, value in get_supplied_items(pr1).items():
396 self.assertEqual(value.qty, 4)
397
398 pr1 = make_purchase_receipt(po.name)
399 pr1.items[0].qty = 2
400 pr1.save()
401 pr1.submit()
402
403 for key, value in get_supplied_items(pr1).items():
404 self.assertEqual(value.qty, 2)
405
406 def test_partial_transfer_serial_no_components_based_on_material_transfer(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530407 """
408 - Set backflush based on Material Transferred for Subcontract
409 - Create subcontracted PO for the item Subcontracted Item SA2.
410 - Transfer the partial components from Stores to Supplier warehouse with serial nos.
411 - Create partial purchase receipt against the PO and change the qty manually.
412 - Transfer the remaining components from Stores to Supplier warehouse with serial nos.
413 - Create purchase receipt for remaining qty against the PO and change the qty manually.
414 """
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530415
Ankush Menat494bd9e2022-03-28 18:52:46 +0530416 set_backflush_based_on("Material Transferred for Subcontract")
417 item_code = "Subcontracted Item SA2"
418 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530419
Ankush Menat494bd9e2022-03-28 18:52:46 +0530420 rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 5}]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530421
422 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530423 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530424 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530425 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530426
427 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530428 d["po_detail"] = po.items[0].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530429
Ankush Menat494bd9e2022-03-28 18:52:46 +0530430 make_stock_transfer_entry(
431 po_no=po.name,
432 main_item_code=item_code,
433 rm_items=rm_items,
434 itemwise_details=copy.deepcopy(itemwise_details),
435 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530436
437 pr1 = make_purchase_receipt(po.name)
438 pr1.items[0].qty = 5
439 pr1.save()
440
441 for key, value in get_supplied_items(pr1).items():
442 details = itemwise_details.get(key)
443 self.assertEqual(value.qty, 3)
444 self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3]))
445
446 pr1.load_from_db()
447 pr1.supplied_items[0].consumed_qty = 5
Ankush Menat494bd9e2022-03-28 18:52:46 +0530448 pr1.supplied_items[0].serial_no = "\n".join(
449 itemwise_details[pr1.supplied_items[0].rm_item_code]["serial_no"]
450 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530451 pr1.save()
452 pr1.submit()
453
454 for key, value in get_supplied_items(pr1).items():
455 details = itemwise_details.get(key)
456 self.assertEqual(value.qty, details.qty)
457 self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
458
459 itemwise_details = make_stock_in_entry(rm_items=rm_items)
460 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530461 d["po_detail"] = po.items[0].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530462
Ankush Menat494bd9e2022-03-28 18:52:46 +0530463 make_stock_transfer_entry(
464 po_no=po.name,
465 main_item_code=item_code,
466 rm_items=rm_items,
467 itemwise_details=copy.deepcopy(itemwise_details),
468 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530469
470 pr1 = make_purchase_receipt(po.name)
471 pr1.submit()
472
473 for key, value in get_supplied_items(pr1).items():
474 details = itemwise_details.get(key)
475 self.assertEqual(value.qty, details.qty)
476 self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
477
Rohit Waghchauree5fb2392021-06-18 20:37:42 +0530478 def test_incorrect_serial_no_components_based_on_material_transfer(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530479 """
480 - Set backflush based on Material Transferred for Subcontract
481 - Create subcontracted PO for the item Subcontracted Item SA2.
482 - Transfer the serialized componenets to the supplier.
483 - Create purchase receipt and change the serial no which is not transferred.
484 - System should throw the error and not allowed to save the purchase receipt.
485 """
Rohit Waghchauree5fb2392021-06-18 20:37:42 +0530486
Ankush Menat494bd9e2022-03-28 18:52:46 +0530487 set_backflush_based_on("Material Transferred for Subcontract")
488 item_code = "Subcontracted Item SA2"
489 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchauree5fb2392021-06-18 20:37:42 +0530490
Ankush Menat494bd9e2022-03-28 18:52:46 +0530491 rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 10}]
Rohit Waghchauree5fb2392021-06-18 20:37:42 +0530492
493 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530494 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530495 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530496 )
Rohit Waghchauree5fb2392021-06-18 20:37:42 +0530497
498 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530499 d["po_detail"] = po.items[0].name
Rohit Waghchauree5fb2392021-06-18 20:37:42 +0530500
Ankush Menat494bd9e2022-03-28 18:52:46 +0530501 make_stock_transfer_entry(
502 po_no=po.name,
503 main_item_code=item_code,
504 rm_items=rm_items,
505 itemwise_details=copy.deepcopy(itemwise_details),
506 )
Rohit Waghchauree5fb2392021-06-18 20:37:42 +0530507
508 pr1 = make_purchase_receipt(po.name)
509 pr1.save()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530510 pr1.supplied_items[0].serial_no = "ABCD"
Rohit Waghchauree5fb2392021-06-18 20:37:42 +0530511 self.assertRaises(frappe.ValidationError, pr1.save)
512 pr1.delete()
513
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530514 def test_partial_transfer_batch_based_on_material_transfer(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530515 """
516 - Set backflush based on Material Transferred for Subcontract
517 - Create subcontracted PO for the item Subcontracted Item SA6.
518 - Transfer the partial components from Stores to Supplier warehouse with batch.
519 - Create partial purchase receipt against the PO and change the qty manually.
520 - Transfer the remaining components from Stores to Supplier warehouse with batch.
521 - Create purchase receipt for remaining qty against the PO and change the qty manually.
522 """
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530523
Ankush Menat494bd9e2022-03-28 18:52:46 +0530524 set_backflush_based_on("Material Transferred for Subcontract")
525 item_code = "Subcontracted Item SA6"
526 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530527
Ankush Menat494bd9e2022-03-28 18:52:46 +0530528 rm_items = [{"item_code": "Subcontracted SRM Item 3", "qty": 5}]
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530529
530 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530531 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530532 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530533 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530534
535 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530536 d["po_detail"] = po.items[0].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530537
Ankush Menat494bd9e2022-03-28 18:52:46 +0530538 make_stock_transfer_entry(
539 po_no=po.name,
540 main_item_code=item_code,
541 rm_items=rm_items,
542 itemwise_details=copy.deepcopy(itemwise_details),
543 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530544
545 pr1 = make_purchase_receipt(po.name)
546 pr1.items[0].qty = 5
547 pr1.save()
548
Ankush Menat494bd9e2022-03-28 18:52:46 +0530549 transferred_batch_no = ""
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530550 for key, value in get_supplied_items(pr1).items():
551 details = itemwise_details.get(key)
552 self.assertEqual(value.qty, 3)
553 transferred_batch_no = details.batch_no
554 self.assertEqual(value.batch_no, details.batch_no)
555
556 pr1.load_from_db()
557 pr1.supplied_items[0].consumed_qty = 5
558 pr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0]
559 pr1.save()
560 pr1.submit()
561
562 for key, value in get_supplied_items(pr1).items():
563 details = itemwise_details.get(key)
564 self.assertEqual(value.qty, details.qty)
565 self.assertEqual(value.batch_no, details.batch_no)
566
567 itemwise_details = make_stock_in_entry(rm_items=rm_items)
568 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530569 d["po_detail"] = po.items[0].name
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530570
Ankush Menat494bd9e2022-03-28 18:52:46 +0530571 make_stock_transfer_entry(
572 po_no=po.name,
573 main_item_code=item_code,
574 rm_items=rm_items,
575 itemwise_details=copy.deepcopy(itemwise_details),
576 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530577
578 pr1 = make_purchase_receipt(po.name)
579 pr1.submit()
580
581 for key, value in get_supplied_items(pr1).items():
582 details = itemwise_details.get(key)
583 self.assertEqual(value.qty, details.qty)
584 self.assertEqual(value.batch_no, details.batch_no)
585
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530586 def test_item_with_batch_based_on_material_transfer_for_purchase_invoice(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530587 """
588 - Set backflush based on Material Transferred for Subcontract
589 - Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
590 - Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
591 - Transfer the components in multiple batches with extra 2 qty for the batched item.
592 - Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
593 - Keep the qty as 2 for Subcontracted Item in the purchase receipt.
594 - In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
595 """
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530596
Ankush Menat494bd9e2022-03-28 18:52:46 +0530597 set_backflush_based_on("Material Transferred for Subcontract")
598 item_code = "Subcontracted Item SA4"
599 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530600
Ankush Menat494bd9e2022-03-28 18:52:46 +0530601 rm_items = [
602 {"item_code": "Subcontracted SRM Item 1", "qty": 10},
603 {"item_code": "Subcontracted SRM Item 2", "qty": 10},
604 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
605 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
606 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
607 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530608 ]
609
610 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530611 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530612 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530613 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530614
615 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530616 d["po_detail"] = po.items[0].name
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530617
Ankush Menat494bd9e2022-03-28 18:52:46 +0530618 make_stock_transfer_entry(
619 po_no=po.name,
620 main_item_code=item_code,
621 rm_items=rm_items,
622 itemwise_details=copy.deepcopy(itemwise_details),
623 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530624
625 pr1 = make_purchase_invoice(po.name)
626 pr1.update_stock = 1
627 pr1.items[0].qty = 2
Ankush Menat494bd9e2022-03-28 18:52:46 +0530628 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530629 add_second_row_in_pr(pr1)
630 pr1.save()
631 pr1.submit()
632
633 for key, value in get_supplied_items(pr1).items():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530634 qty = 4 if key != "Subcontracted SRM Item 3" else 6
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530635 self.assertEqual(value.qty, qty)
636
637 pr1 = make_purchase_invoice(po.name)
638 pr1.update_stock = 1
Ankush Menat494bd9e2022-03-28 18:52:46 +0530639 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530640 pr1.items[0].qty = 2
641 add_second_row_in_pr(pr1)
642 pr1.save()
643 pr1.submit()
644
645 for key, value in get_supplied_items(pr1).items():
646 self.assertEqual(value.qty, 4)
647
648 pr1 = make_purchase_invoice(po.name)
649 pr1.update_stock = 1
650 pr1.items[0].qty = 2
Ankush Menat494bd9e2022-03-28 18:52:46 +0530651 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530652 pr1.save()
653 pr1.submit()
654
655 for key, value in get_supplied_items(pr1).items():
656 self.assertEqual(value.qty, 2)
657
Ankush Menat494bd9e2022-03-28 18:52:46 +0530658 def test_partial_transfer_serial_no_components_based_on_material_transfer_for_purchase_invoice(
659 self,
660 ):
661 """
662 - Set backflush based on Material Transferred for Subcontract
663 - Create subcontracted PO for the item Subcontracted Item SA2.
664 - Transfer the partial components from Stores to Supplier warehouse with serial nos.
665 - Create partial purchase receipt against the PO and change the qty manually.
666 - Transfer the remaining components from Stores to Supplier warehouse with serial nos.
667 - Create purchase receipt for remaining qty against the PO and change the qty manually.
668 """
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530669
Ankush Menat494bd9e2022-03-28 18:52:46 +0530670 set_backflush_based_on("Material Transferred for Subcontract")
671 item_code = "Subcontracted Item SA2"
672 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530673
Ankush Menat494bd9e2022-03-28 18:52:46 +0530674 rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 5}]
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530675
676 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530677 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530678 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530679 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530680
681 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530682 d["po_detail"] = po.items[0].name
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530683
Ankush Menat494bd9e2022-03-28 18:52:46 +0530684 make_stock_transfer_entry(
685 po_no=po.name,
686 main_item_code=item_code,
687 rm_items=rm_items,
688 itemwise_details=copy.deepcopy(itemwise_details),
689 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530690
691 pr1 = make_purchase_invoice(po.name)
692 pr1.update_stock = 1
693 pr1.items[0].qty = 5
Ankush Menat494bd9e2022-03-28 18:52:46 +0530694 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530695 pr1.save()
696
697 for key, value in get_supplied_items(pr1).items():
698 details = itemwise_details.get(key)
699 self.assertEqual(value.qty, 3)
700 self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3]))
701
702 pr1.load_from_db()
703 pr1.supplied_items[0].consumed_qty = 5
Ankush Menat494bd9e2022-03-28 18:52:46 +0530704 pr1.supplied_items[0].serial_no = "\n".join(
705 itemwise_details[pr1.supplied_items[0].rm_item_code]["serial_no"]
706 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530707 pr1.save()
708 pr1.submit()
709
710 for key, value in get_supplied_items(pr1).items():
711 details = itemwise_details.get(key)
712 self.assertEqual(value.qty, details.qty)
713 self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
714
715 itemwise_details = make_stock_in_entry(rm_items=rm_items)
716 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530717 d["po_detail"] = po.items[0].name
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530718
Ankush Menat494bd9e2022-03-28 18:52:46 +0530719 make_stock_transfer_entry(
720 po_no=po.name,
721 main_item_code=item_code,
722 rm_items=rm_items,
723 itemwise_details=copy.deepcopy(itemwise_details),
724 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530725
726 pr1 = make_purchase_invoice(po.name)
727 pr1.update_stock = 1
Ankush Menat494bd9e2022-03-28 18:52:46 +0530728 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530729 pr1.submit()
730
731 for key, value in get_supplied_items(pr1).items():
732 details = itemwise_details.get(key)
733 self.assertEqual(value.qty, details.qty)
734 self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
735
736 def test_partial_transfer_batch_based_on_material_transfer_for_purchase_invoice(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530737 """
738 - Set backflush based on Material Transferred for Subcontract
739 - Create subcontracted PO for the item Subcontracted Item SA6.
740 - Transfer the partial components from Stores to Supplier warehouse with batch.
741 - Create partial purchase receipt against the PO and change the qty manually.
742 - Transfer the remaining components from Stores to Supplier warehouse with batch.
743 - Create purchase receipt for remaining qty against the PO and change the qty manually.
744 """
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530745
Ankush Menat494bd9e2022-03-28 18:52:46 +0530746 set_backflush_based_on("Material Transferred for Subcontract")
747 item_code = "Subcontracted Item SA6"
748 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530749
Ankush Menat494bd9e2022-03-28 18:52:46 +0530750 rm_items = [{"item_code": "Subcontracted SRM Item 3", "qty": 5}]
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530751
752 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530753 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530754 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530755 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530756
757 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530758 d["po_detail"] = po.items[0].name
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530759
Ankush Menat494bd9e2022-03-28 18:52:46 +0530760 make_stock_transfer_entry(
761 po_no=po.name,
762 main_item_code=item_code,
763 rm_items=rm_items,
764 itemwise_details=copy.deepcopy(itemwise_details),
765 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530766
767 pr1 = make_purchase_invoice(po.name)
768 pr1.update_stock = 1
769 pr1.items[0].qty = 5
Ankush Menat494bd9e2022-03-28 18:52:46 +0530770 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530771 pr1.save()
772
Ankush Menat494bd9e2022-03-28 18:52:46 +0530773 transferred_batch_no = ""
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530774 for key, value in get_supplied_items(pr1).items():
775 details = itemwise_details.get(key)
776 self.assertEqual(value.qty, 3)
777 transferred_batch_no = details.batch_no
778 self.assertEqual(value.batch_no, details.batch_no)
779
780 pr1.load_from_db()
781 pr1.supplied_items[0].consumed_qty = 5
782 pr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0]
783 pr1.save()
784 pr1.submit()
785
786 for key, value in get_supplied_items(pr1).items():
787 details = itemwise_details.get(key)
788 self.assertEqual(value.qty, details.qty)
789 self.assertEqual(value.batch_no, details.batch_no)
790
791 itemwise_details = make_stock_in_entry(rm_items=rm_items)
792 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530793 d["po_detail"] = po.items[0].name
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530794
Ankush Menat494bd9e2022-03-28 18:52:46 +0530795 make_stock_transfer_entry(
796 po_no=po.name,
797 main_item_code=item_code,
798 rm_items=rm_items,
799 itemwise_details=copy.deepcopy(itemwise_details),
800 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530801
802 pr1 = make_purchase_invoice(po.name)
803 pr1.update_stock = 1
Ankush Menat494bd9e2022-03-28 18:52:46 +0530804 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530805 pr1.submit()
806
807 for key, value in get_supplied_items(pr1).items():
808 details = itemwise_details.get(key)
809 self.assertEqual(value.qty, details.qty)
810 self.assertEqual(value.batch_no, details.batch_no)
811
812 def test_item_with_batch_based_on_bom_for_purchase_invoice(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530813 """
814 - Set backflush based on BOM
815 - Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
816 - Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
817 - Transfer the components in multiple batches.
818 - Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
819 - Keep the qty as 2 for Subcontracted Item in the purchase receipt.
820 """
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530821
Ankush Menat494bd9e2022-03-28 18:52:46 +0530822 set_backflush_based_on("BOM")
823 item_code = "Subcontracted Item SA4"
824 items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530825
Ankush Menat494bd9e2022-03-28 18:52:46 +0530826 rm_items = [
827 {"item_code": "Subcontracted SRM Item 1", "qty": 10},
828 {"item_code": "Subcontracted SRM Item 2", "qty": 10},
829 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
830 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
831 {"item_code": "Subcontracted SRM Item 3", "qty": 3},
832 {"item_code": "Subcontracted SRM Item 3", "qty": 1},
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530833 ]
834
835 itemwise_details = make_stock_in_entry(rm_items=rm_items)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530836 po = create_purchase_order(
Sagar Sharmad074c932022-03-31 19:57:42 +0530837 rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530838 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530839
840 for d in rm_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530841 d["po_detail"] = po.items[0].name
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530842
Ankush Menat494bd9e2022-03-28 18:52:46 +0530843 make_stock_transfer_entry(
844 po_no=po.name,
845 main_item_code=item_code,
846 rm_items=rm_items,
847 itemwise_details=copy.deepcopy(itemwise_details),
848 )
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530849
850 pr1 = make_purchase_invoice(po.name)
851 pr1.update_stock = 1
852 pr1.items[0].qty = 2
Ankush Menat494bd9e2022-03-28 18:52:46 +0530853 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530854 add_second_row_in_pr(pr1)
855 pr1.save()
856 pr1.submit()
857
858 for key, value in get_supplied_items(pr1).items():
859 self.assertEqual(value.qty, 4)
860
861 pr1 = make_purchase_invoice(po.name)
862 pr1.update_stock = 1
863 pr1.items[0].qty = 2
Ankush Menat494bd9e2022-03-28 18:52:46 +0530864 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530865 add_second_row_in_pr(pr1)
866 pr1.save()
867 pr1.submit()
868
869 for key, value in get_supplied_items(pr1).items():
870 self.assertEqual(value.qty, 4)
871
872 pr1 = make_purchase_invoice(po.name)
873 pr1.update_stock = 1
874 pr1.items[0].qty = 2
Ankush Menat494bd9e2022-03-28 18:52:46 +0530875 pr1.items[0].expense_account = "Stock Adjustment - _TC"
Rohit Waghchaure110e1522021-06-15 17:29:52 +0530876 pr1.save()
877 pr1.submit()
878
879 for key, value in get_supplied_items(pr1).items():
880 self.assertEqual(value.qty, 2)
881
Ankush Menat494bd9e2022-03-28 18:52:46 +0530882
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530883def add_second_row_in_pr(pr):
884 item_dict = {}
Ankush Menat494bd9e2022-03-28 18:52:46 +0530885 for column in [
886 "item_code",
887 "item_name",
888 "qty",
889 "uom",
890 "warehouse",
891 "stock_uom",
892 "purchase_order",
893 "purchase_order_item",
894 "conversion_factor",
895 "rate",
896 "expense_account",
897 "po_detail",
898 ]:
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530899 item_dict[column] = pr.items[0].get(column)
900
Ankush Menat494bd9e2022-03-28 18:52:46 +0530901 pr.append("items", item_dict)
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530902 pr.set_missing_values()
903
Ankush Menat494bd9e2022-03-28 18:52:46 +0530904
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530905def get_supplied_items(pr_doc):
906 supplied_items = {}
Ankush Menat494bd9e2022-03-28 18:52:46 +0530907 for row in pr_doc.get("supplied_items"):
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530908 if row.rm_item_code not in supplied_items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530909 supplied_items.setdefault(
910 row.rm_item_code, frappe._dict({"qty": 0, "serial_no": [], "batch_no": defaultdict(float)})
911 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530912
913 details = supplied_items[row.rm_item_code]
914 update_item_details(row, details)
915
916 return supplied_items
917
Ankush Menat494bd9e2022-03-28 18:52:46 +0530918
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530919def make_stock_in_entry(**args):
920 args = frappe._dict(args)
921
922 items = {}
923 for row in args.rm_items:
924 row = frappe._dict(row)
925
Ankush Menat494bd9e2022-03-28 18:52:46 +0530926 doc = make_stock_entry(
927 target=row.warehouse or "_Test Warehouse - _TC",
928 item_code=row.item_code,
929 qty=row.qty or 1,
930 basic_rate=row.rate or 100,
931 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530932
933 if row.item_code not in items:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530934 items.setdefault(
935 row.item_code, frappe._dict({"qty": 0, "serial_no": [], "batch_no": defaultdict(float)})
936 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530937
938 child_row = doc.items[0]
939 details = items[child_row.item_code]
940 update_item_details(child_row, details)
941
942 return items
943
Ankush Menat494bd9e2022-03-28 18:52:46 +0530944
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530945def update_item_details(child_row, details):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530946 details.qty += (
947 child_row.get("qty")
948 if child_row.doctype == "Stock Entry Detail"
949 else child_row.get("consumed_qty")
950 )
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530951
952 if child_row.serial_no:
953 details.serial_no.extend(get_serial_nos(child_row.serial_no))
954
955 if child_row.batch_no:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530956 details.batch_no[child_row.batch_no] += child_row.get("qty") or child_row.get("consumed_qty")
957
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530958
959def make_stock_transfer_entry(**args):
960 args = frappe._dict(args)
961
962 items = []
963 for row in args.rm_items:
964 row = frappe._dict(row)
965
Ankush Menat494bd9e2022-03-28 18:52:46 +0530966 item = {
967 "item_code": row.main_item_code or args.main_item_code,
968 "rm_item_code": row.item_code,
969 "qty": row.qty or 1,
970 "item_name": row.item_code,
971 "rate": row.rate or 100,
972 "stock_uom": row.stock_uom or "Nos",
973 "warehouse": row.warehuose or "_Test Warehouse - _TC",
974 }
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530975
976 item_details = args.itemwise_details.get(row.item_code)
977
978 if item_details and item_details.serial_no:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530979 serial_nos = item_details.serial_no[0 : cint(row.qty)]
980 item["serial_no"] = "\n".join(serial_nos)
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530981 item_details.serial_no = list(set(item_details.serial_no) - set(serial_nos))
982
983 if item_details and item_details.batch_no:
984 for batch_no, batch_qty in item_details.batch_no.items():
985 if batch_qty >= row.qty:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530986 item["batch_no"] = batch_no
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +0530987 item_details.batch_no[batch_no] -= row.qty
988 break
989
990 items.append(item)
991
992 ste_dict = make_rm_stock_entry(args.po_no, items)
993 doc = frappe.get_doc(ste_dict)
994 doc.insert()
995 doc.submit()
996
997 return doc
998
Ankush Menat494bd9e2022-03-28 18:52:46 +0530999
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301000def make_subcontract_items():
Ankush Menat494bd9e2022-03-28 18:52:46 +05301001 sub_contracted_items = {
1002 "Subcontracted Item SA1": {},
1003 "Subcontracted Item SA2": {},
1004 "Subcontracted Item SA3": {},
1005 "Subcontracted Item SA4": {
1006 "has_batch_no": 1,
1007 "create_new_batch": 1,
1008 "batch_number_series": "SBAT.####",
1009 },
1010 "Subcontracted Item SA5": {},
1011 "Subcontracted Item SA6": {},
1012 }
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301013
1014 for item, properties in sub_contracted_items.items():
Ankush Menat494bd9e2022-03-28 18:52:46 +05301015 if not frappe.db.exists("Item", item):
1016 properties.update({"is_stock_item": 1, "is_sub_contracted_item": 1})
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301017 make_item(item, properties)
1018
Ankush Menat494bd9e2022-03-28 18:52:46 +05301019
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301020def make_raw_materials():
Ankush Menat494bd9e2022-03-28 18:52:46 +05301021 raw_materials = {
1022 "Subcontracted SRM Item 1": {},
1023 "Subcontracted SRM Item 2": {"has_serial_no": 1, "serial_no_series": "SRI.####"},
1024 "Subcontracted SRM Item 3": {
1025 "has_batch_no": 1,
1026 "create_new_batch": 1,
1027 "batch_number_series": "BAT.####",
1028 },
1029 "Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
1030 "Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
1031 }
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301032
1033 for item, properties in raw_materials.items():
Ankush Menat494bd9e2022-03-28 18:52:46 +05301034 if not frappe.db.exists("Item", item):
1035 properties.update({"is_stock_item": 1})
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301036 make_item(item, properties)
1037
Ankush Menat494bd9e2022-03-28 18:52:46 +05301038
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301039def make_bom_for_subcontracted_items():
1040 boms = {
Ankush Menat494bd9e2022-03-28 18:52:46 +05301041 "Subcontracted Item SA1": [
1042 "Subcontracted SRM Item 1",
1043 "Subcontracted SRM Item 2",
1044 "Subcontracted SRM Item 3",
1045 ],
1046 "Subcontracted Item SA2": ["Subcontracted SRM Item 2"],
1047 "Subcontracted Item SA3": ["Subcontracted SRM Item 2"],
1048 "Subcontracted Item SA4": [
1049 "Subcontracted SRM Item 1",
1050 "Subcontracted SRM Item 2",
1051 "Subcontracted SRM Item 3",
1052 ],
1053 "Subcontracted Item SA5": ["Subcontracted SRM Item 5"],
1054 "Subcontracted Item SA6": ["Subcontracted SRM Item 3"],
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301055 }
1056
1057 for item_code, raw_materials in boms.items():
Ankush Menat494bd9e2022-03-28 18:52:46 +05301058 if not frappe.db.exists("BOM", {"item": item_code}):
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301059 make_bom(item=item_code, raw_materials=raw_materials, rate=100)
1060
Ankush Menat494bd9e2022-03-28 18:52:46 +05301061
Rohit Waghchaure7b7ceaa2021-05-24 20:11:15 +05301062def set_backflush_based_on(based_on):
Ankush Menat494bd9e2022-03-28 18:52:46 +05301063 frappe.db.set_value(
1064 "Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", based_on
1065 )