blob: e11d56722670196b1d2997c4ad5cb111816f7034 [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
25from webnotes.model.doc import Document
26from webnotes.model.code import get_obj
27from webnotes.utils import cstr, flt
Nabin Hait88f9cb52011-09-30 17:20:06 +053028from webnotes.model.doclist import getlist
Nabin Hait31665e52011-09-29 10:41:02 +053029sql = webnotes.conn.sql
30
31from sandbox.testdata.masters import *
32from sandbox.testdata import stock_entry
33#----------------------------------------------------------
34
Nabin Hait88f9cb52011-09-30 17:20:06 +053035
Nabin Hait31665e52011-09-29 10:41:02 +053036class TestStockEntry(unittest.TestCase):
Nabin Hait88f9cb52011-09-30 17:20:06 +053037 #===========================================================================
38 def assertDoc(self, lst):
39 """assert all values"""
40 for d in lst:
41 cl, vl = [], []
42 for k in d.keys():
43 if k!='doctype':
44 cl.append('%s=%s' % (k, '%s'))
45 vl.append(d[k])
46
47 self.assertTrue(sql("select name from `tab%s` where %s limit 1" % (d['doctype'], ' and '.join(cl)), vl))
48
49 #===========================================================================
50 def assertCount(self, lst):
51 """assert all values"""
52 for d in lst:
53 cl, vl = [], []
54 for k in d[0].keys():
55 if k!='doctype':
56 cl.append('%s=%s' % (k, '%s'))
57 vl.append(d[0][k])
58
59 self.assertTrue(sql("select count(name) from `tab%s` where %s limit 1" % (d[0]['doctype'], ' and '.join(cl)), vl)[0][0] == d[1])
60
61 #===========================================================================
Nabin Hait31665e52011-09-29 10:41:02 +053062 def setUp(self):
63 print "====================================="
Nabin Hait88f9cb52011-09-30 17:20:06 +053064 webnotes.conn.begin()
Nabin Hait31665e52011-09-29 10:41:02 +053065 create_master_records()
66 print 'Master Data Created'
67
Nabin Hait88f9cb52011-09-30 17:20:06 +053068 #===========================================================================
69 # Purpose: Material Receipt
70 #===========================================================================
71 def test_mr_onsubmit(self):
Nabin Haita970b112011-09-30 18:05:08 +053072 print "Test Case: Material Receipt submission"
Nabin Hait88f9cb52011-09-30 17:20:06 +053073 self.save_stock_entry('Material Receipt')
74
75 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
76 self.submit_stock_entry(mr)
77
78 # stock ledger entry
79 print "Checking stock ledger entry........."
80 self.assertDoc(self.get_expected_sle('mr_submit'))
81
82 # bin qty
83 print "Checking Bin qty........."
84 self.assertDoc([{'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'}])
85
86 # serial no
87 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]])
88
89
90 #===========================================================================
91 def test_mr_oncancel(self):
Nabin Haita970b112011-09-30 18:05:08 +053092 print "Test Case: Material Receipt Cancellation"
Nabin Hait88f9cb52011-09-30 17:20:06 +053093 self.save_stock_entry('Material Receipt')
94
95 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
96 self.cancel_stock_entry(mr)
97
98 # stock ledger entry
99 print "Checking stock ledger entry........."
100 self.assertDoc(self.get_expected_sle('mr_cancel'))
101
102 # bin qty
103 print "Checking Bin qty........."
104 self.assertDoc([{'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh1'}])
105
106 # serial no
107 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': '', 'status': 'Not in Use', 'docstatus': 2}, 10]])
108
109 #===========================================================================
110 # Purpose: Material Transafer
111 #===========================================================================
112 def test_mtn_onsubmit(self):
Nabin Haita970b112011-09-30 18:05:08 +0530113 print "Test Case: Material Transfer Note submission"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530114
115 self.save_stock_entry('Material Receipt')
116 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
117 mr = self.submit_stock_entry(mr)
118
119 self.save_stock_entry('Material Transfer')
120 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
Nabin Hait2cca0e72011-11-09 12:12:53 +0530121 mtn = self.submit_stock_entry(mtn)
Nabin Hait88f9cb52011-09-30 17:20:06 +0530122
123 # stock ledger entry
124 print "Checking stock ledger entry........."
125 self.assertDoc(self.get_expected_sle('mtn_submit'))
126
127 # bin qty
128 print "Checking Bin qty........."
129 self.assertDoc([
130 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh1'},
131 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh2'}
132 ])
133
134 # serial no
135 self.assertCount([
136 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 5],
137 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh2', 'status': 'In Store', 'docstatus': 0}, 5]
138 ])
139
140 #===========================================================================
141 def test_mtn_oncancel(self):
Nabin Haita970b112011-09-30 18:05:08 +0530142 print "Test Case: Material Transfer Note Cancellation"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530143
144 self.save_stock_entry('Material Receipt')
145 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
146 mr = self.submit_stock_entry(mr)
147
148 self.save_stock_entry('Material Transfer')
149 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
150 self.cancel_stock_entry(mtn)
151
152 # stock ledger entry
153 print "Checking stock ledger entry........."
154 self.assertDoc(self.get_expected_sle('mtn_cancel'))
155
156 # bin qty
157 print "Checking Bin qty........."
158 self.assertDoc([
159 {'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'},
160 {'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh2'}
161 ])
162
163 # serial no
164 self.assertCount([[{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]])
Nabin Haita970b112011-09-30 18:05:08 +0530165
166#===========================================================================
167 # Purpose: Material Issue
168 #===========================================================================
169 def test_mi_onsubmit(self):
170 print "Test Case: Material Issue submission"
171
172 self.save_stock_entry('Material Receipt')
173 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
174 mr = self.submit_stock_entry(mr)
175
176 self.save_stock_entry('Material Issue')
177 mi = get_obj('Stock Entry', stock_entry.mi[0].name, with_children=1)
178 mi = self.submit_stock_entry(mi)
179
180 # stock ledger entry
181 print "Checking stock ledger entry........."
182 self.assertDoc(self.get_expected_sle('mi_submit'))
183
184 # bin qty
185 print "Checking Bin qty........."
186 self.assertDoc([
187 {'doctype':'Bin', 'actual_qty':6, 'item_code':'it', 'warehouse':'wh1'}
188 ])
189
190 # serial no
191 self.assertCount([
192 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 6]
193 ])
194
195 #===========================================================================
196 def test_mi_oncancel(self):
197 print "Test Case: Material Issue Cancellation"
198
199 self.save_stock_entry('Material Receipt')
200 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
201 mr = self.submit_stock_entry(mr)
202
203 self.save_stock_entry('Material Issue')
204 mi = get_obj('Stock Entry', stock_entry.mi[0].name, with_children=1)
205 self.cancel_stock_entry(mi)
206
207 # stock ledger entry
208 print "Checking stock ledger entry........."
209 self.assertDoc(self.get_expected_sle('mi_cancel'))
210
211 # bin qty
212 print "Checking Bin qty........."
213 self.assertDoc([
214 {'doctype':'Bin', 'actual_qty':10, 'item_code':'it', 'warehouse':'wh1'}
215 ])
216
217 # serial no
218 self.assertCount([
219 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10]
220 ])
221
Nabin Hait2cca0e72011-11-09 12:12:53 +0530222 #===========================================================================
223 def test_entries_on_same_datetime(self):
224 print "Test Case: Multiple entries on same datetime, cancel first one"
Nabin Haita970b112011-09-30 18:05:08 +0530225
Nabin Hait2cca0e72011-11-09 12:12:53 +0530226 # submitted 1st MR
227 self.save_stock_entry('Material Receipt')
228 mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1)
229 mr = self.submit_stock_entry(mr)
230
231 # submitted 2nd MR
232 for each in stock_entry.mr1:
233 each.save(1)
234 for t in stock_entry.mr1[1:]:
235 sql("update `tabStock Entry Detail` set parent = '%s' where name = '%s'" % (stock_entry.mr1[0].name, t.name))
236
237 mr1 = get_obj('Stock Entry', stock_entry.mr1[0].name, with_children=1)
238 mr1 = self.submit_stock_entry(mr1)
Nabin Hait88f9cb52011-09-30 17:20:06 +0530239
Nabin Hait2cca0e72011-11-09 12:12:53 +0530240
241 # submitted MTN
242 self.save_stock_entry('Material Transfer')
243 mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1)
244 mtn = self.submit_stock_entry(mtn)
245
246 # cancel prev MR
247 mr.on_cancel()
248 mr.doc.cancel_reason = "testing"
249 mr.doc.docstatus = 2
250 mr.doc.save()
251
252
253 # stock ledger entry
254 print "Checking stock ledger entry........."
255 self.assertDoc(self.get_expected_sle('entries_on_same_datetime'))
256
257 # bin qty
258 print "Checking Bin qty........."
259 self.assertDoc([
260 {'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh1'},
261 {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh2'}
262 ])
263
264 # serial no
265 self.assertCount([
266 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 0],
267 [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh2', 'status': 'In Store', 'docstatus': 0}, 5]
268 ])
269
Nabin Hait88f9cb52011-09-30 17:20:06 +0530270 #===========================================================================
271 def save_stock_entry(self, t):
272 if t == 'Material Receipt':
273 data = stock_entry.mr
274 elif t == 'Material Transfer':
275 data = stock_entry.mtn
Nabin Haita970b112011-09-30 18:05:08 +0530276 elif t == 'Material Issue':
277 data = stock_entry.mi
Nabin Hait88f9cb52011-09-30 17:20:06 +0530278
279 for each in data:
Nabin Hait31665e52011-09-29 10:41:02 +0530280 each.save(1)
281
Nabin Hait88f9cb52011-09-30 17:20:06 +0530282 for t in data[1:]:
283 sql("update `tabStock Entry Detail` set parent = '%s' where name = '%s'" % (data[0].name, t.name))
Nabin Hait31665e52011-09-29 10:41:02 +0530284 print "Stock Entry Created"
285
Nabin Hait31665e52011-09-29 10:41:02 +0530286
287 #===========================================================================
Nabin Hait88f9cb52011-09-30 17:20:06 +0530288 def submit_stock_entry(self, ste):
289 ste.validate()
290 ste.on_submit()
Nabin Hait31665e52011-09-29 10:41:02 +0530291
Nabin Hait88f9cb52011-09-30 17:20:06 +0530292 ste.doc.docstatus = 1
293 ste.doc.save()
294
Nabin Hait31665e52011-09-29 10:41:02 +0530295 print "Stock Entry Submitted"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530296 return ste
Nabin Hait31665e52011-09-29 10:41:02 +0530297
298 #===========================================================================
Nabin Hait88f9cb52011-09-30 17:20:06 +0530299 def cancel_stock_entry(self, ste):
300 ste = self.submit_stock_entry(ste)
Nabin Hait31665e52011-09-29 10:41:02 +0530301
Nabin Hait88f9cb52011-09-30 17:20:06 +0530302 ste.on_cancel()
Nabin Hait31665e52011-09-29 10:41:02 +0530303
Nabin Hait88f9cb52011-09-30 17:20:06 +0530304 ste.doc.cancel_reason = "testing"
305 ste.doc.docstatus = 2
306 ste.doc.save()
Nabin Hait31665e52011-09-29 10:41:02 +0530307
308 print "Stock Entry Cancelled"
Nabin Hait88f9cb52011-09-30 17:20:06 +0530309 return ste
Nabin Hait31665e52011-09-29 10:41:02 +0530310
311 #===========================================================================
Nabin Hait31665e52011-09-29 10:41:02 +0530312 def tearDown(self):
313 webnotes.conn.rollback()
Nabin Hait88f9cb52011-09-30 17:20:06 +0530314
315
316 # Expected Result Set
317 #===================================================================================================
318 def get_expected_sle(self, action):
319 expected_sle = {
320 'mr_submit': [{
321 'doctype': 'Stock Ledger Entry',
322 'item_code':'it',
323 'warehouse':'wh1',
324 'voucher_type': 'Stock Entry',
325 'voucher_no': stock_entry.mr[0].name,
326 'actual_qty': 10,
327 'bin_aqat': 10,
328 'valuation_rate': 100,
329 'is_cancelled': 'No'
330 }],
331 'mr_cancel': [{
332 'doctype': 'Stock Ledger Entry',
333 'item_code':'it',
334 'warehouse':'wh1',
335 'voucher_type': 'Stock Entry',
336 'voucher_no': stock_entry.mr[0].name,
337 'actual_qty': 10,
338 'bin_aqat': 10,
339 'valuation_rate': 100,
340 'is_cancelled': 'Yes'
341 },{
342 'doctype': 'Stock Ledger Entry',
343 'item_code':'it',
344 'warehouse':'wh1',
345 'voucher_type': 'Stock Entry',
346 'voucher_no': stock_entry.mr[0].name,
347 'actual_qty': -10,
348 'ifnull(bin_aqat, 0)': 0,
349 'ifnull(valuation_rate, 0)': 0,
350 "ifnull(is_cancelled, 'No')": 'Yes'
351 }],
352 'mtn_submit': [{
353 'doctype': 'Stock Ledger Entry',
354 'item_code':'it',
355 'warehouse':'wh1',
356 'voucher_type': 'Stock Entry',
357 'voucher_no': stock_entry.mtn[0].name,
358 'actual_qty': -5,
359 'bin_aqat': 5,
360 'valuation_rate': 100,
361 'is_cancelled': 'No'
362 }, {
363 'doctype': 'Stock Ledger Entry',
364 'item_code':'it',
365 'warehouse':'wh2',
366 'voucher_type': 'Stock Entry',
367 'voucher_no': stock_entry.mtn[0].name,
368 'actual_qty': 5,
369 'bin_aqat': 5,
370 'valuation_rate': 100,
371 'is_cancelled': 'No'
372 }],
373 'mtn_cancel': [{
374 'doctype': 'Stock Ledger Entry',
375 'item_code':'it',
376 'warehouse':'wh1',
377 'voucher_type': 'Stock Entry',
378 'voucher_no': stock_entry.mtn[0].name,
379 'actual_qty': -5,
380 'bin_aqat': 5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530381 'is_cancelled': 'Yes'
382 }, {
383 'doctype': 'Stock Ledger Entry',
384 'item_code':'it',
385 'warehouse':'wh2',
386 'voucher_type': 'Stock Entry',
387 'voucher_no': stock_entry.mtn[0].name,
388 'actual_qty': 5,
389 'bin_aqat': 5,
390 'valuation_rate': 100,
391 'is_cancelled': 'Yes'
392 }, {
393 'doctype': 'Stock Ledger Entry',
394 'item_code':'it',
395 'warehouse':'wh1',
396 'voucher_type': 'Stock Entry',
397 'voucher_no': stock_entry.mtn[0].name,
398 'actual_qty': 5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530399 'is_cancelled': 'Yes'
400 }, {
401 'doctype': 'Stock Ledger Entry',
402 'item_code':'it',
403 'warehouse':'wh2',
404 'voucher_type': 'Stock Entry',
405 'voucher_no': stock_entry.mtn[0].name,
406 'actual_qty': -5,
Nabin Hait88f9cb52011-09-30 17:20:06 +0530407 'is_cancelled': 'Yes'
Nabin Haita970b112011-09-30 18:05:08 +0530408 }],
409 'mi_submit': [{'doctype': 'Stock Ledger Entry',
410 'item_code':'it',
411 'warehouse':'wh1',
412 'voucher_type': 'Stock Entry',
413 'voucher_no': stock_entry.mi[0].name,
414 'actual_qty': -4,
415 'bin_aqat': 6,
416 'valuation_rate': 100,
417 'is_cancelled': 'No'
418 }],
419 'mi_cancel': [{
420 'doctype': 'Stock Ledger Entry',
421 'item_code':'it',
422 'warehouse':'wh1',
423 'voucher_type': 'Stock Entry',
424 'voucher_no': stock_entry.mi[0].name,
425 'actual_qty': -4,
426 'bin_aqat': 6,
427 'valuation_rate': 100,
428 'is_cancelled': 'Yes'
429 },{
430 'doctype': 'Stock Ledger Entry',
431 'item_code':'it',
432 'warehouse':'wh1',
433 'voucher_type': 'Stock Entry',
434 'voucher_no': stock_entry.mi[0].name,
435 'actual_qty': 4,
436 'ifnull(bin_aqat, 0)': 0,
437 'ifnull(valuation_rate, 0)': 0,
438 "ifnull(is_cancelled, 'No')": 'Yes'
Nabin Hait2cca0e72011-11-09 12:12:53 +0530439 }],
440 'entries_on_same_datetime': [{
441 'doctype': 'Stock Ledger Entry',
442 'item_code':'it',
443 'warehouse':'wh1',
444 'voucher_type': 'Stock Entry',
445 'voucher_no': stock_entry.mr[0].name,
446 'actual_qty': 10,
447 'bin_aqat': 10,
448 'valuation_rate': 100,
449 'is_cancelled': 'Yes'
450 }, {
451 'doctype': 'Stock Ledger Entry',
452 'item_code':'it',
453 'warehouse':'wh1',
454 'voucher_type': 'Stock Entry',
455 'voucher_no': stock_entry.mr[0].name,
456 'actual_qty': -10,
457 'ifnull(bin_aqat, 0)': 0,
458 'ifnull(valuation_rate, 0)': 0,
459 "ifnull(is_cancelled, 'No')": 'Yes'
460 }, {
461 'doctype': 'Stock Ledger Entry',
462 'item_code':'it',
463 'warehouse':'wh1',
464 'voucher_type': 'Stock Entry',
465 'voucher_no': stock_entry.mr1[0].name,
466 'actual_qty': 5,
467 'bin_aqat': 5,
468 'valuation_rate': 400,
469 'is_cancelled': 'No'
470 }, {
471 'doctype': 'Stock Ledger Entry',
472 'item_code':'it',
473 'warehouse':'wh1',
474 'voucher_type': 'Stock Entry',
475 'voucher_no': stock_entry.mtn[0].name,
476 'actual_qty': -5,
477 'bin_aqat': 0,
478 'valuation_rate': 400,
479 'is_cancelled': 'No'
480 }, {
481 'doctype': 'Stock Ledger Entry',
482 'item_code':'it',
483 'warehouse':'wh2',
484 'voucher_type': 'Stock Entry',
485 'voucher_no': stock_entry.mtn[0].name,
486 'actual_qty': 5,
487 'bin_aqat': 5,
488 'valuation_rate': 100,
489 'is_cancelled': 'No'
Nabin Hait88f9cb52011-09-30 17:20:06 +0530490 }]
491 }
492
493 return expected_sle[action]