blob: 7721a3ebdbf0949a291cc7dc6a957542ca302693 [file] [log] [blame]
Rushabh Mehta3966f1d2012-02-23 12:35:32 +05301# ERPNext - web based ERP (http://erpnext.com)
2# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
Nabin Hait31665e52011-09-29 10:41:02 +053017import unittest
18
19import webnotes
20import webnotes.profile
21webnotes.user = webnotes.profile.Profile()
22
23
24from webnotes.model.doc import Document
25from webnotes.model.code import get_obj
26from webnotes.utils import cstr, flt
Nabin Hait88f9cb52011-09-30 17:20:06 +053027from webnotes.model.doclist import getlist
Nabin Hait31665e52011-09-29 10:41:02 +053028sql = webnotes.conn.sql
29
30from sandbox.testdata.masters import *
31from sandbox.testdata import stock_entry
32#----------------------------------------------------------
33
Nabin Hait88f9cb52011-09-30 17:20:06 +053034
Nabin Hait31665e52011-09-29 10:41:02 +053035class TestStockEntry(unittest.TestCase):
Nabin Hait88f9cb52011-09-30 17:20:06 +053036 #===========================================================================
37 def assertDoc(self, lst):
38 """assert all values"""
39 for d in lst:
40 cl, vl = [], []
41 for k in d.keys():
42 if k!='doctype':
43 cl.append('%s=%s' % (k, '%s'))
44 vl.append(d[k])
45
46 self.assertTrue(sql("select name from `tab%s` where %s limit 1" % (d['doctype'], ' and '.join(cl)), vl))
47
48 #===========================================================================
49 def assertCount(self, lst):
50 """assert all values"""
51 for d in lst:
52 cl, vl = [], []
53 for k in d[0].keys():
54 if k!='doctype':
55 cl.append('%s=%s' % (k, '%s'))
56 vl.append(d[0][k])
57
58 self.assertTrue(sql("select count(name) from `tab%s` where %s limit 1" % (d[0]['doctype'], ' and '.join(cl)), vl)[0][0] == d[1])
59
60 #===========================================================================
Nabin Hait31665e52011-09-29 10:41:02 +053061 def setUp(self):
62 print "====================================="
Nabin Hait88f9cb52011-09-30 17:20:06 +053063 webnotes.conn.begin()
Nabin Hait31665e52011-09-29 10:41:02 +053064 create_master_records()
65 print 'Master Data Created'
66
Nabin Hait88f9cb52011-09-30 17:20:06 +053067 #===========================================================================
68 # Purpose: Material Receipt
69 #===========================================================================
70 def test_mr_onsubmit(self):
Nabin Haita970b112011-09-30 18:05:08 +053071 print "Test Case: Material Receipt submission"
Nabin Hait88f9cb52011-09-30 17:20:06 +053072 self.save_stock_entry('Material Receipt')
73
74 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
75 self.submit_stock_entry(mr)
76
77 # stock ledger entry
78 print "Checking stock ledger entry........."
79 self.assertDoc(self.get_expected_sle('mr_submit'))
80
81 # bin qty
82 print "Checking Bin qty........."
83 self.assertDoc([{'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'}])
84
85 # serial no
86 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]])
87
88
89 #===========================================================================
90 def test_mr_oncancel(self):
Nabin Haita970b112011-09-30 18:05:08 +053091 print "Test Case: Material Receipt Cancellation"
Nabin Hait88f9cb52011-09-30 17:20:06 +053092 self.save_stock_entry('Material Receipt')
93
94 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
95 self.cancel_stock_entry(mr)
96
97 # stock ledger entry
98 print "Checking stock ledger entry........."
99 self.assertDoc(self.get_expected_sle('mr_cancel'))
100
101 # bin qty
102 print "Checking Bin qty........."
103 self.assertDoc([{'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh1'}])
104
105 # serial no
106 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': '', 'status': 'Not in Use', 'docstatus': 2}, 10]])
107
108 #===========================================================================
109 # Purpose: Material Transafer
110 #===========================================================================
111 def test_mtn_onsubmit(self):
Nabin Haita970b112011-09-30 18:05:08 +0530112 print "Test Case: Material Transfer Note submission"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530113
114 self.save_stock_entry('Material Receipt')
115 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
116 mr = self.submit_stock_entry(mr)
117
118 self.save_stock_entry('Material Transfer')
119 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
Nabin Hait2cca0e72011-11-09 12:12:53 +0530120 mtn = self.submit_stock_entry(mtn)
Nabin Hait88f9cb52011-09-30 17:20:06 +0530121
122 # stock ledger entry
123 print "Checking stock ledger entry........."
124 self.assertDoc(self.get_expected_sle('mtn_submit'))
125
126 # bin qty
127 print "Checking Bin qty........."
128 self.assertDoc([
129 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh1'},
130 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh2'}
131 ])
132
133 # serial no
134 self.assertCount([
135 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 5],
136 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh2', 'status': 'In Store', 'docstatus': 0}, 5]
137 ])
138
139 #===========================================================================
140 def test_mtn_oncancel(self):
Nabin Haita970b112011-09-30 18:05:08 +0530141 print "Test Case: Material Transfer Note Cancellation"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530142
143 self.save_stock_entry('Material Receipt')
144 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
145 mr = self.submit_stock_entry(mr)
146
147 self.save_stock_entry('Material Transfer')
148 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
149 self.cancel_stock_entry(mtn)
150
151 # stock ledger entry
152 print "Checking stock ledger entry........."
153 self.assertDoc(self.get_expected_sle('mtn_cancel'))
154
155 # bin qty
156 print "Checking Bin qty........."
157 self.assertDoc([
158 {'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'},
159 {'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh2'}
160 ])
161
162 # serial no
163 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]])
Nabin Haita970b112011-09-30 18:05:08 +0530164
165#===========================================================================
166 # Purpose: Material Issue
167 #===========================================================================
168 def test_mi_onsubmit(self):
169 print "Test Case: Material Issue submission"
170
171 self.save_stock_entry('Material Receipt')
172 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
173 mr = self.submit_stock_entry(mr)
174
175 self.save_stock_entry('Material Issue')
176 mi = get_obj('Stock Entry', stock_entry.mi[0].name, with_children=1)
177 mi = self.submit_stock_entry(mi)
178
179 # stock ledger entry
180 print "Checking stock ledger entry........."
181 self.assertDoc(self.get_expected_sle('mi_submit'))
182
183 # bin qty
184 print "Checking Bin qty........."
185 self.assertDoc([
186 {'doctype':'Bin', 'actual_qty':6, 'item_code':'it', 'warehouse':'wh1'}
187 ])
188
189 # serial no
190 self.assertCount([
191 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 6]
192 ])
193
194 #===========================================================================
195 def test_mi_oncancel(self):
196 print "Test Case: Material Issue Cancellation"
197
198 self.save_stock_entry('Material Receipt')
199 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
200 mr = self.submit_stock_entry(mr)
201
202 self.save_stock_entry('Material Issue')
203 mi = get_obj('Stock Entry', stock_entry.mi[0].name, with_children=1)
204 self.cancel_stock_entry(mi)
205
206 # stock ledger entry
207 print "Checking stock ledger entry........."
208 self.assertDoc(self.get_expected_sle('mi_cancel'))
209
210 # bin qty
211 print "Checking Bin qty........."
212 self.assertDoc([
213 {'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'}
214 ])
215
216 # serial no
217 self.assertCount([
218 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]
219 ])
220
Nabin Hait2cca0e72011-11-09 12:12:53 +0530221 #===========================================================================
222 def test_entries_on_same_datetime(self):
223 print "Test Case: Multiple entries on same datetime, cancel first one"
Nabin Haita970b112011-09-30 18:05:08 +0530224
Nabin Hait2cca0e72011-11-09 12:12:53 +0530225 # submitted 1st MR
226 self.save_stock_entry('Material Receipt')
227 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
228 mr = self.submit_stock_entry(mr)
229
230 # submitted 2nd MR
231 for each in stock_entry.mr1:
232 each.save(1)
233 for t in stock_entry.mr1[1:]:
234 sql("update `tabStock Entry Detail` set parent = '%s' where name = '%s'" % (stock_entry.mr1[0].name, t.name))
235
236 mr1 = get_obj('Stock Entry', stock_entry.mr1[0].name, with_children=1)
237 mr1 = self.submit_stock_entry(mr1)
Nabin Hait88f9cb52011-09-30 17:20:06 +0530238
Nabin Hait2cca0e72011-11-09 12:12:53 +0530239
240 # submitted MTN
241 self.save_stock_entry('Material Transfer')
242 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
243 mtn = self.submit_stock_entry(mtn)
244
245 # cancel prev MR
246 mr.on_cancel()
247 mr.doc.cancel_reason = "testing"
248 mr.doc.docstatus = 2
249 mr.doc.save()
250
251
252 # stock ledger entry
253 print "Checking stock ledger entry........."
254 self.assertDoc(self.get_expected_sle('entries_on_same_datetime'))
255
256 # bin qty
257 print "Checking Bin qty........."
258 self.assertDoc([
259 {'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh1'},
260 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh2'}
261 ])
262
263 # serial no
264 self.assertCount([
265 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 0],
266 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh2', 'status': 'In Store', 'docstatus': 0}, 5]
267 ])
268
Nabin Hait88f9cb52011-09-30 17:20:06 +0530269 #===========================================================================
270 def save_stock_entry(self, t):
271 if t == 'Material Receipt':
272 data = stock_entry.mr
273 elif t == 'Material Transfer':
274 data = stock_entry.mtn
Nabin Haita970b112011-09-30 18:05:08 +0530275 elif t == 'Material Issue':
276 data = stock_entry.mi
Nabin Hait88f9cb52011-09-30 17:20:06 +0530277
278 for each in data:
Nabin Hait31665e52011-09-29 10:41:02 +0530279 each.save(1)
280
Nabin Hait88f9cb52011-09-30 17:20:06 +0530281 for t in data[1:]:
282 sql("update `tabStock Entry Detail` set parent = '%s' where name = '%s'" % (data[0].name, t.name))
Nabin Hait31665e52011-09-29 10:41:02 +0530283 print "Stock Entry Created"
284
Nabin Hait31665e52011-09-29 10:41:02 +0530285
286 #===========================================================================
Nabin Hait88f9cb52011-09-30 17:20:06 +0530287 def submit_stock_entry(self, ste):
288 ste.validate()
289 ste.on_submit()
Nabin Hait31665e52011-09-29 10:41:02 +0530290
Nabin Hait88f9cb52011-09-30 17:20:06 +0530291 ste.doc.docstatus = 1
292 ste.doc.save()
293
Nabin Hait31665e52011-09-29 10:41:02 +0530294 print "Stock Entry Submitted"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530295 return ste
Nabin Hait31665e52011-09-29 10:41:02 +0530296
297 #===========================================================================
Nabin Hait88f9cb52011-09-30 17:20:06 +0530298 def cancel_stock_entry(self, ste):
299 ste = self.submit_stock_entry(ste)
Nabin Hait31665e52011-09-29 10:41:02 +0530300
Nabin Hait88f9cb52011-09-30 17:20:06 +0530301 ste.on_cancel()
Nabin Hait31665e52011-09-29 10:41:02 +0530302
Nabin Hait88f9cb52011-09-30 17:20:06 +0530303 ste.doc.cancel_reason = "testing"
304 ste.doc.docstatus = 2
305 ste.doc.save()
Nabin Hait31665e52011-09-29 10:41:02 +0530306
307 print "Stock Entry Cancelled"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530308 return ste
Nabin Hait31665e52011-09-29 10:41:02 +0530309
310 #===========================================================================
Nabin Hait31665e52011-09-29 10:41:02 +0530311 def tearDown(self):
312 webnotes.conn.rollback()
Nabin Hait88f9cb52011-09-30 17:20:06 +0530313
314
315 # Expected Result Set
316 #===================================================================================================
317 def get_expected_sle(self, action):
318 expected_sle = {
319 'mr_submit': [{
320 'doctype': 'Stock Ledger Entry',
321 'item_code':'it',
322 'warehouse':'wh1',
323 'voucher_type': 'Stock Entry',
324 'voucher_no': stock_entry.mr[0].name,
325 'actual_qty': 10,
326 'bin_aqat': 10,
327 'valuation_rate': 100,
328 'is_cancelled': 'No'
329 }],
330 'mr_cancel': [{
331 'doctype': 'Stock Ledger Entry',
332 'item_code':'it',
333 'warehouse':'wh1',
334 'voucher_type': 'Stock Entry',
335 'voucher_no': stock_entry.mr[0].name,
336 'actual_qty': 10,
337 'bin_aqat': 10,
338 'valuation_rate': 100,
339 'is_cancelled': 'Yes'
340 },{
341 'doctype': 'Stock Ledger Entry',
342 'item_code':'it',
343 'warehouse':'wh1',
344 'voucher_type': 'Stock Entry',
345 'voucher_no': stock_entry.mr[0].name,
346 'actual_qty': -10,
347 'ifnull(bin_aqat, 0)': 0,
348 'ifnull(valuation_rate, 0)': 0,
349 "ifnull(is_cancelled, 'No')": 'Yes'
350 }],
351 'mtn_submit': [{
352 'doctype': 'Stock Ledger Entry',
353 'item_code':'it',
354 'warehouse':'wh1',
355 'voucher_type': 'Stock Entry',
356 'voucher_no': stock_entry.mtn[0].name,
357 'actual_qty': -5,
358 'bin_aqat': 5,
359 'valuation_rate': 100,
360 'is_cancelled': 'No'
361 }, {
362 'doctype': 'Stock Ledger Entry',
363 'item_code':'it',
364 'warehouse':'wh2',
365 'voucher_type': 'Stock Entry',
366 'voucher_no': stock_entry.mtn[0].name,
367 'actual_qty': 5,
368 'bin_aqat': 5,
369 'valuation_rate': 100,
370 'is_cancelled': 'No'
371 }],
372 'mtn_cancel': [{
373 'doctype': 'Stock Ledger Entry',
374 'item_code':'it',
375 'warehouse':'wh1',
376 'voucher_type': 'Stock Entry',
377 'voucher_no': stock_entry.mtn[0].name,
378 'actual_qty': -5,
379 'bin_aqat': 5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530380 'is_cancelled': 'Yes'
381 }, {
382 'doctype': 'Stock Ledger Entry',
383 'item_code':'it',
384 'warehouse':'wh2',
385 'voucher_type': 'Stock Entry',
386 'voucher_no': stock_entry.mtn[0].name,
387 'actual_qty': 5,
388 'bin_aqat': 5,
389 'valuation_rate': 100,
390 'is_cancelled': 'Yes'
391 }, {
392 'doctype': 'Stock Ledger Entry',
393 'item_code':'it',
394 'warehouse':'wh1',
395 'voucher_type': 'Stock Entry',
396 'voucher_no': stock_entry.mtn[0].name,
397 'actual_qty': 5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530398 'is_cancelled': 'Yes'
399 }, {
400 'doctype': 'Stock Ledger Entry',
401 'item_code':'it',
402 'warehouse':'wh2',
403 'voucher_type': 'Stock Entry',
404 'voucher_no': stock_entry.mtn[0].name,
405 'actual_qty': -5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530406 'is_cancelled': 'Yes'
Nabin Haita970b112011-09-30 18:05:08 +0530407 }],
408 'mi_submit': [{'doctype': 'Stock Ledger Entry',
409 'item_code':'it',
410 'warehouse':'wh1',
411 'voucher_type': 'Stock Entry',
412 'voucher_no': stock_entry.mi[0].name,
413 'actual_qty': -4,
414 'bin_aqat': 6,
415 'valuation_rate': 100,
416 'is_cancelled': 'No'
417 }],
418 'mi_cancel': [{
419 'doctype': 'Stock Ledger Entry',
420 'item_code':'it',
421 'warehouse':'wh1',
422 'voucher_type': 'Stock Entry',
423 'voucher_no': stock_entry.mi[0].name,
424 'actual_qty': -4,
425 'bin_aqat': 6,
426 'valuation_rate': 100,
427 'is_cancelled': 'Yes'
428 },{
429 'doctype': 'Stock Ledger Entry',
430 'item_code':'it',
431 'warehouse':'wh1',
432 'voucher_type': 'Stock Entry',
433 'voucher_no': stock_entry.mi[0].name,
434 'actual_qty': 4,
435 'ifnull(bin_aqat, 0)': 0,
436 'ifnull(valuation_rate, 0)': 0,
437 "ifnull(is_cancelled, 'No')": 'Yes'
Nabin Hait2cca0e72011-11-09 12:12:53 +0530438 }],
439 'entries_on_same_datetime': [{
440 'doctype': 'Stock Ledger Entry',
441 'item_code':'it',
442 'warehouse':'wh1',
443 'voucher_type': 'Stock Entry',
444 'voucher_no': stock_entry.mr[0].name,
445 'actual_qty': 10,
446 'bin_aqat': 10,
447 'valuation_rate': 100,
448 'is_cancelled': 'Yes'
449 }, {
450 'doctype': 'Stock Ledger Entry',
451 'item_code':'it',
452 'warehouse':'wh1',
453 'voucher_type': 'Stock Entry',
454 'voucher_no': stock_entry.mr[0].name,
455 'actual_qty': -10,
456 'ifnull(bin_aqat, 0)': 0,
457 'ifnull(valuation_rate, 0)': 0,
458 "ifnull(is_cancelled, 'No')": 'Yes'
459 }, {
460 'doctype': 'Stock Ledger Entry',
461 'item_code':'it',
462 'warehouse':'wh1',
463 'voucher_type': 'Stock Entry',
464 'voucher_no': stock_entry.mr1[0].name,
465 'actual_qty': 5,
466 'bin_aqat': 5,
467 'valuation_rate': 400,
468 'is_cancelled': 'No'
469 }, {
470 'doctype': 'Stock Ledger Entry',
471 'item_code':'it',
472 'warehouse':'wh1',
473 'voucher_type': 'Stock Entry',
474 'voucher_no': stock_entry.mtn[0].name,
475 'actual_qty': -5,
476 'bin_aqat': 0,
477 'valuation_rate': 400,
478 'is_cancelled': 'No'
479 }, {
480 'doctype': 'Stock Ledger Entry',
481 'item_code':'it',
482 'warehouse':'wh2',
483 'voucher_type': 'Stock Entry',
484 'voucher_no': stock_entry.mtn[0].name,
485 'actual_qty': 5,
486 'bin_aqat': 5,
487 'valuation_rate': 100,
488 'is_cancelled': 'No'
Nabin Hait88f9cb52011-09-30 17:20:06 +0530489 }]
490 }
491
492 return expected_sle[action]