blob: fe28880b93a35cf7b4f07c909ebd005fa91e1b98 [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
Anand Doshi486f9df2012-07-19 13:40:31 +053017from __future__ import unicode_literals
Nabin Hait31665e52011-09-29 10:41:02 +053018import unittest
19
20import webnotes
21import webnotes.profile
22webnotes.user = webnotes.profile.Profile()
23
24
Nabin Hait31665e52011-09-29 10:41:02 +053025from webnotes.model.code import get_obj
Nabin Hait31665e52011-09-29 10:41:02 +053026sql = webnotes.conn.sql
27
28from sandbox.testdata.masters import *
29from sandbox.testdata import stock_entry
30#----------------------------------------------------------
31
Nabin Hait88f9cb52011-09-30 17:20:06 +053032
Nabin Hait31665e52011-09-29 10:41:02 +053033class TestStockEntry(unittest.TestCase):
Nabin Hait88f9cb52011-09-30 17:20:06 +053034 #===========================================================================
35 def assertDoc(self, lst):
36 """assert all values"""
37 for d in lst:
38 cl, vl = [], []
39 for k in d.keys():
40 if k!='doctype':
41 cl.append('%s=%s' % (k, '%s'))
42 vl.append(d[k])
43
44 self.assertTrue(sql("select name from `tab%s` where %s limit 1" % (d['doctype'], ' and '.join(cl)), vl))
45
46 #===========================================================================
47 def assertCount(self, lst):
48 """assert all values"""
49 for d in lst:
50 cl, vl = [], []
51 for k in d[0].keys():
52 if k!='doctype':
53 cl.append('%s=%s' % (k, '%s'))
54 vl.append(d[0][k])
55
56 self.assertTrue(sql("select count(name) from `tab%s` where %s limit 1" % (d[0]['doctype'], ' and '.join(cl)), vl)[0][0] == d[1])
57
58 #===========================================================================
Nabin Hait31665e52011-09-29 10:41:02 +053059 def setUp(self):
60 print "====================================="
Nabin Hait88f9cb52011-09-30 17:20:06 +053061 webnotes.conn.begin()
Nabin Hait31665e52011-09-29 10:41:02 +053062 create_master_records()
63 print 'Master Data Created'
64
Nabin Hait88f9cb52011-09-30 17:20:06 +053065 #===========================================================================
66 # Purpose: Material Receipt
67 #===========================================================================
68 def test_mr_onsubmit(self):
Nabin Haita970b112011-09-30 18:05:08 +053069 print "Test Case: Material Receipt submission"
Nabin Hait88f9cb52011-09-30 17:20:06 +053070 self.save_stock_entry('Material Receipt')
71
72 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
73 self.submit_stock_entry(mr)
74
75 # stock ledger entry
76 print "Checking stock ledger entry........."
77 self.assertDoc(self.get_expected_sle('mr_submit'))
78
79 # bin qty
80 print "Checking Bin qty........."
81 self.assertDoc([{'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'}])
82
83 # serial no
84 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]])
85
86
87 #===========================================================================
88 def test_mr_oncancel(self):
Nabin Haita970b112011-09-30 18:05:08 +053089 print "Test Case: Material Receipt Cancellation"
Nabin Hait88f9cb52011-09-30 17:20:06 +053090 self.save_stock_entry('Material Receipt')
91
92 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
93 self.cancel_stock_entry(mr)
94
95 # stock ledger entry
96 print "Checking stock ledger entry........."
97 self.assertDoc(self.get_expected_sle('mr_cancel'))
98
99 # bin qty
100 print "Checking Bin qty........."
101 self.assertDoc([{'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh1'}])
102
103 # serial no
104 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': '', 'status': 'Not in Use', 'docstatus': 2}, 10]])
105
106 #===========================================================================
107 # Purpose: Material Transafer
108 #===========================================================================
109 def test_mtn_onsubmit(self):
Nabin Haita970b112011-09-30 18:05:08 +0530110 print "Test Case: Material Transfer Note submission"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530111
112 self.save_stock_entry('Material Receipt')
113 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
114 mr = self.submit_stock_entry(mr)
115
116 self.save_stock_entry('Material Transfer')
117 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
Nabin Hait2cca0e72011-11-09 12:12:53 +0530118 mtn = self.submit_stock_entry(mtn)
Nabin Hait88f9cb52011-09-30 17:20:06 +0530119
120 # stock ledger entry
121 print "Checking stock ledger entry........."
122 self.assertDoc(self.get_expected_sle('mtn_submit'))
123
124 # bin qty
125 print "Checking Bin qty........."
126 self.assertDoc([
127 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh1'},
128 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh2'}
129 ])
130
131 # serial no
132 self.assertCount([
133 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 5],
134 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh2', 'status': 'In Store', 'docstatus': 0}, 5]
135 ])
136
137 #===========================================================================
138 def test_mtn_oncancel(self):
Nabin Haita970b112011-09-30 18:05:08 +0530139 print "Test Case: Material Transfer Note Cancellation"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530140
141 self.save_stock_entry('Material Receipt')
142 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
143 mr = self.submit_stock_entry(mr)
144
145 self.save_stock_entry('Material Transfer')
146 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
147 self.cancel_stock_entry(mtn)
148
149 # stock ledger entry
150 print "Checking stock ledger entry........."
151 self.assertDoc(self.get_expected_sle('mtn_cancel'))
152
153 # bin qty
154 print "Checking Bin qty........."
155 self.assertDoc([
156 {'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'},
157 {'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh2'}
158 ])
159
160 # serial no
161 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]])
Nabin Haita970b112011-09-30 18:05:08 +0530162
163#===========================================================================
164 # Purpose: Material Issue
165 #===========================================================================
166 def test_mi_onsubmit(self):
167 print "Test Case: Material Issue submission"
168
169 self.save_stock_entry('Material Receipt')
170 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
171 mr = self.submit_stock_entry(mr)
172
173 self.save_stock_entry('Material Issue')
174 mi = get_obj('Stock Entry', stock_entry.mi[0].name, with_children=1)
175 mi = self.submit_stock_entry(mi)
176
177 # stock ledger entry
178 print "Checking stock ledger entry........."
179 self.assertDoc(self.get_expected_sle('mi_submit'))
180
181 # bin qty
182 print "Checking Bin qty........."
183 self.assertDoc([
184 {'doctype':'Bin', 'actual_qty':6, 'item_code':'it', 'warehouse':'wh1'}
185 ])
186
187 # serial no
188 self.assertCount([
189 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 6]
190 ])
191
192 #===========================================================================
193 def test_mi_oncancel(self):
194 print "Test Case: Material Issue Cancellation"
195
196 self.save_stock_entry('Material Receipt')
197 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
198 mr = self.submit_stock_entry(mr)
199
200 self.save_stock_entry('Material Issue')
201 mi = get_obj('Stock Entry', stock_entry.mi[0].name, with_children=1)
202 self.cancel_stock_entry(mi)
203
204 # stock ledger entry
205 print "Checking stock ledger entry........."
206 self.assertDoc(self.get_expected_sle('mi_cancel'))
207
208 # bin qty
209 print "Checking Bin qty........."
210 self.assertDoc([
211 {'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'}
212 ])
213
214 # serial no
215 self.assertCount([
216 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]
217 ])
218
Nabin Hait2cca0e72011-11-09 12:12:53 +0530219 #===========================================================================
220 def test_entries_on_same_datetime(self):
221 print "Test Case: Multiple entries on same datetime, cancel first one"
Nabin Haita970b112011-09-30 18:05:08 +0530222
Nabin Hait2cca0e72011-11-09 12:12:53 +0530223 # submitted 1st MR
224 self.save_stock_entry('Material Receipt')
225 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
226 mr = self.submit_stock_entry(mr)
227
228 # submitted 2nd MR
229 for each in stock_entry.mr1:
230 each.save(1)
231 for t in stock_entry.mr1[1:]:
232 sql("update `tabStock Entry Detail` set parent = '%s' where name = '%s'" % (stock_entry.mr1[0].name, t.name))
233
234 mr1 = get_obj('Stock Entry', stock_entry.mr1[0].name, with_children=1)
235 mr1 = self.submit_stock_entry(mr1)
Nabin Hait88f9cb52011-09-30 17:20:06 +0530236
Nabin Hait2cca0e72011-11-09 12:12:53 +0530237
238 # submitted MTN
239 self.save_stock_entry('Material Transfer')
240 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
241 mtn = self.submit_stock_entry(mtn)
242
243 # cancel prev MR
244 mr.on_cancel()
245 mr.doc.cancel_reason = "testing"
246 mr.doc.docstatus = 2
247 mr.doc.save()
248
249
250 # stock ledger entry
251 print "Checking stock ledger entry........."
252 self.assertDoc(self.get_expected_sle('entries_on_same_datetime'))
253
254 # bin qty
255 print "Checking Bin qty........."
256 self.assertDoc([
257 {'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh1'},
258 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh2'}
259 ])
260
261 # serial no
262 self.assertCount([
263 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 0],
264 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh2', 'status': 'In Store', 'docstatus': 0}, 5]
265 ])
266
Nabin Hait88f9cb52011-09-30 17:20:06 +0530267 #===========================================================================
268 def save_stock_entry(self, t):
269 if t == 'Material Receipt':
270 data = stock_entry.mr
271 elif t == 'Material Transfer':
272 data = stock_entry.mtn
Nabin Haita970b112011-09-30 18:05:08 +0530273 elif t == 'Material Issue':
274 data = stock_entry.mi
Nabin Hait88f9cb52011-09-30 17:20:06 +0530275
276 for each in data:
Nabin Hait31665e52011-09-29 10:41:02 +0530277 each.save(1)
278
Nabin Hait88f9cb52011-09-30 17:20:06 +0530279 for t in data[1:]:
280 sql("update `tabStock Entry Detail` set parent = '%s' where name = '%s'" % (data[0].name, t.name))
Nabin Hait31665e52011-09-29 10:41:02 +0530281 print "Stock Entry Created"
282
Nabin Hait31665e52011-09-29 10:41:02 +0530283
284 #===========================================================================
Nabin Hait88f9cb52011-09-30 17:20:06 +0530285 def submit_stock_entry(self, ste):
286 ste.validate()
287 ste.on_submit()
Nabin Hait31665e52011-09-29 10:41:02 +0530288
Nabin Hait88f9cb52011-09-30 17:20:06 +0530289 ste.doc.docstatus = 1
290 ste.doc.save()
291
Nabin Hait31665e52011-09-29 10:41:02 +0530292 print "Stock Entry Submitted"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530293 return ste
Nabin Hait31665e52011-09-29 10:41:02 +0530294
295 #===========================================================================
Nabin Hait88f9cb52011-09-30 17:20:06 +0530296 def cancel_stock_entry(self, ste):
297 ste = self.submit_stock_entry(ste)
Nabin Hait31665e52011-09-29 10:41:02 +0530298
Nabin Hait88f9cb52011-09-30 17:20:06 +0530299 ste.on_cancel()
Nabin Hait31665e52011-09-29 10:41:02 +0530300
Nabin Hait88f9cb52011-09-30 17:20:06 +0530301 ste.doc.cancel_reason = "testing"
302 ste.doc.docstatus = 2
303 ste.doc.save()
Nabin Hait31665e52011-09-29 10:41:02 +0530304
305 print "Stock Entry Cancelled"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530306 return ste
Nabin Hait31665e52011-09-29 10:41:02 +0530307
308 #===========================================================================
Nabin Hait31665e52011-09-29 10:41:02 +0530309 def tearDown(self):
310 webnotes.conn.rollback()
Nabin Hait88f9cb52011-09-30 17:20:06 +0530311
312
313 # Expected Result Set
314 #===================================================================================================
315 def get_expected_sle(self, action):
316 expected_sle = {
317 'mr_submit': [{
318 'doctype': 'Stock Ledger Entry',
319 'item_code':'it',
320 'warehouse':'wh1',
321 'voucher_type': 'Stock Entry',
322 'voucher_no': stock_entry.mr[0].name,
323 'actual_qty': 10,
324 'bin_aqat': 10,
325 'valuation_rate': 100,
326 'is_cancelled': 'No'
327 }],
328 'mr_cancel': [{
329 'doctype': 'Stock Ledger Entry',
330 'item_code':'it',
331 'warehouse':'wh1',
332 'voucher_type': 'Stock Entry',
333 'voucher_no': stock_entry.mr[0].name,
334 'actual_qty': 10,
335 'bin_aqat': 10,
336 'valuation_rate': 100,
337 'is_cancelled': 'Yes'
338 },{
339 'doctype': 'Stock Ledger Entry',
340 'item_code':'it',
341 'warehouse':'wh1',
342 'voucher_type': 'Stock Entry',
343 'voucher_no': stock_entry.mr[0].name,
344 'actual_qty': -10,
345 'ifnull(bin_aqat, 0)': 0,
346 'ifnull(valuation_rate, 0)': 0,
347 "ifnull(is_cancelled, 'No')": 'Yes'
348 }],
349 'mtn_submit': [{
350 'doctype': 'Stock Ledger Entry',
351 'item_code':'it',
352 'warehouse':'wh1',
353 'voucher_type': 'Stock Entry',
354 'voucher_no': stock_entry.mtn[0].name,
355 'actual_qty': -5,
356 'bin_aqat': 5,
357 'valuation_rate': 100,
358 'is_cancelled': 'No'
359 }, {
360 'doctype': 'Stock Ledger Entry',
361 'item_code':'it',
362 'warehouse':'wh2',
363 'voucher_type': 'Stock Entry',
364 'voucher_no': stock_entry.mtn[0].name,
365 'actual_qty': 5,
366 'bin_aqat': 5,
367 'valuation_rate': 100,
368 'is_cancelled': 'No'
369 }],
370 'mtn_cancel': [{
371 'doctype': 'Stock Ledger Entry',
372 'item_code':'it',
373 'warehouse':'wh1',
374 'voucher_type': 'Stock Entry',
375 'voucher_no': stock_entry.mtn[0].name,
376 'actual_qty': -5,
377 'bin_aqat': 5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530378 'is_cancelled': 'Yes'
379 }, {
380 'doctype': 'Stock Ledger Entry',
381 'item_code':'it',
382 'warehouse':'wh2',
383 'voucher_type': 'Stock Entry',
384 'voucher_no': stock_entry.mtn[0].name,
385 'actual_qty': 5,
386 'bin_aqat': 5,
387 'valuation_rate': 100,
388 'is_cancelled': 'Yes'
389 }, {
390 'doctype': 'Stock Ledger Entry',
391 'item_code':'it',
392 'warehouse':'wh1',
393 'voucher_type': 'Stock Entry',
394 'voucher_no': stock_entry.mtn[0].name,
395 'actual_qty': 5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530396 'is_cancelled': 'Yes'
397 }, {
398 'doctype': 'Stock Ledger Entry',
399 'item_code':'it',
400 'warehouse':'wh2',
401 'voucher_type': 'Stock Entry',
402 'voucher_no': stock_entry.mtn[0].name,
403 'actual_qty': -5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530404 'is_cancelled': 'Yes'
Nabin Haita970b112011-09-30 18:05:08 +0530405 }],
406 'mi_submit': [{'doctype': 'Stock Ledger Entry',
407 'item_code':'it',
408 'warehouse':'wh1',
409 'voucher_type': 'Stock Entry',
410 'voucher_no': stock_entry.mi[0].name,
411 'actual_qty': -4,
412 'bin_aqat': 6,
413 'valuation_rate': 100,
414 'is_cancelled': 'No'
415 }],
416 'mi_cancel': [{
417 'doctype': 'Stock Ledger Entry',
418 'item_code':'it',
419 'warehouse':'wh1',
420 'voucher_type': 'Stock Entry',
421 'voucher_no': stock_entry.mi[0].name,
422 'actual_qty': -4,
423 'bin_aqat': 6,
424 'valuation_rate': 100,
425 'is_cancelled': 'Yes'
426 },{
427 'doctype': 'Stock Ledger Entry',
428 'item_code':'it',
429 'warehouse':'wh1',
430 'voucher_type': 'Stock Entry',
431 'voucher_no': stock_entry.mi[0].name,
432 'actual_qty': 4,
433 'ifnull(bin_aqat, 0)': 0,
434 'ifnull(valuation_rate, 0)': 0,
435 "ifnull(is_cancelled, 'No')": 'Yes'
Nabin Hait2cca0e72011-11-09 12:12:53 +0530436 }],
437 'entries_on_same_datetime': [{
438 'doctype': 'Stock Ledger Entry',
439 'item_code':'it',
440 'warehouse':'wh1',
441 'voucher_type': 'Stock Entry',
442 'voucher_no': stock_entry.mr[0].name,
443 'actual_qty': 10,
444 'bin_aqat': 10,
445 'valuation_rate': 100,
446 'is_cancelled': 'Yes'
447 }, {
448 'doctype': 'Stock Ledger Entry',
449 'item_code':'it',
450 'warehouse':'wh1',
451 'voucher_type': 'Stock Entry',
452 'voucher_no': stock_entry.mr[0].name,
453 'actual_qty': -10,
454 'ifnull(bin_aqat, 0)': 0,
455 'ifnull(valuation_rate, 0)': 0,
456 "ifnull(is_cancelled, 'No')": 'Yes'
457 }, {
458 'doctype': 'Stock Ledger Entry',
459 'item_code':'it',
460 'warehouse':'wh1',
461 'voucher_type': 'Stock Entry',
462 'voucher_no': stock_entry.mr1[0].name,
463 'actual_qty': 5,
464 'bin_aqat': 5,
465 'valuation_rate': 400,
466 'is_cancelled': 'No'
467 }, {
468 'doctype': 'Stock Ledger Entry',
469 'item_code':'it',
470 'warehouse':'wh1',
471 'voucher_type': 'Stock Entry',
472 'voucher_no': stock_entry.mtn[0].name,
473 'actual_qty': -5,
474 'bin_aqat': 0,
475 'valuation_rate': 400,
476 'is_cancelled': 'No'
477 }, {
478 'doctype': 'Stock Ledger Entry',
479 'item_code':'it',
480 'warehouse':'wh2',
481 'voucher_type': 'Stock Entry',
482 'voucher_no': stock_entry.mtn[0].name,
483 'actual_qty': 5,
484 'bin_aqat': 5,
485 'valuation_rate': 100,
486 'is_cancelled': 'No'
Nabin Hait88f9cb52011-09-30 17:20:06 +0530487 }]
488 }
489
490 return expected_sle[action]