blob: b9e3cd2e824be767cea14dfa5e68995f8849ae75 [file] [log] [blame]
Anand Doshi885e0742015-03-03 14:55:30 +05301# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
Nabin Haiteea2b342013-10-11 18:31:33 +05302# License: GNU General Public License v3. See license.txt
3
4from __future__ import unicode_literals
Rushabh Mehta793ba6b2014-02-14 15:47:51 +05305import frappe
Nabin Haiteea2b342013-10-11 18:31:33 +05306
Nabin Hait62985362014-04-04 12:05:16 +05307from frappe.utils import flt, cstr, nowdate, nowtime
8from erpnext.stock.utils import update_bin
9from erpnext.stock.stock_ledger import update_entries_after
10from erpnext.accounts.utils import get_fiscal_year
Nabin Haiteea2b342013-10-11 18:31:33 +053011
Nabin Haitb7e46c42015-10-12 16:46:29 +053012def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False):
Nabin Haiteea2b342013-10-11 18:31:33 +053013 """
14 Repost everything!
15 """
Anand Doshie9baaa62014-02-26 12:35:33 +053016 frappe.db.auto_commit_on_many_writes = 1
Nabin Hait62985362014-04-04 12:05:16 +053017
Nabin Haitca471f42013-11-20 13:14:12 +053018 if allow_negative_stock:
Nabin Hait249bbbc2014-11-26 15:35:08 +053019 existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
20 frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
Nabin Hait62985362014-04-04 12:05:16 +053021
22 for d in frappe.db.sql("""select distinct item_code, warehouse from
Nabin Hait4ed7f682013-10-23 11:50:09 +053023 (select item_code, warehouse from tabBin
24 union
Nabin Haitc2caae52013-10-23 12:02:08 +053025 select item_code, warehouse from `tabStock Ledger Entry`) a"""):
Nabin Hait8a28ccf2014-10-14 11:41:44 +053026 try:
Nabin Haitb7e46c42015-10-12 16:46:29 +053027 repost_stock(d[0], d[1], allow_zero_rate, only_actual, only_bin)
Nabin Hait8a28ccf2014-10-14 11:41:44 +053028 frappe.db.commit()
29 except:
30 frappe.db.rollback()
Nabin Hait62985362014-04-04 12:05:16 +053031
Nabin Haitca471f42013-11-20 13:14:12 +053032 if allow_negative_stock:
Nabin Hait249bbbc2014-11-26 15:35:08 +053033 frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
Anand Doshie9baaa62014-02-26 12:35:33 +053034 frappe.db.auto_commit_on_many_writes = 0
Nabin Haiteea2b342013-10-11 18:31:33 +053035
Nabin Haitb7e46c42015-10-12 16:46:29 +053036def repost_stock(item_code, warehouse, allow_zero_rate=False, only_actual=False, only_bin=False):
37 if not only_bin:
Rushabh Mehtac4d4c7f2015-10-14 17:37:28 +053038 repost_actual_qty(item_code, warehouse, allow_zero_rate)
Nabin Hait62985362014-04-04 12:05:16 +053039
Nabin Hait2348a5f2014-10-15 15:31:33 +053040 if item_code and warehouse and not only_actual:
Nabin Haitb7e46c42015-10-12 16:46:29 +053041 qty_dict = {
Nabin Haiteea2b342013-10-11 18:31:33 +053042 "reserved_qty": get_reserved_qty(item_code, warehouse),
43 "indented_qty": get_indented_qty(item_code, warehouse),
44 "ordered_qty": get_ordered_qty(item_code, warehouse),
45 "planned_qty": get_planned_qty(item_code, warehouse)
Nabin Haitb7e46c42015-10-12 16:46:29 +053046 }
47 if only_bin:
48 qty_dict.update({
49 "actual_qty": get_balance_qty_from_sle(item_code, warehouse)
50 })
Rushabh Mehtac4d4c7f2015-10-14 17:37:28 +053051
Nabin Haitb7e46c42015-10-12 16:46:29 +053052 update_bin_qty(item_code, warehouse, qty_dict)
Nabin Haiteea2b342013-10-11 18:31:33 +053053
Nabin Haitf1a07ff2014-10-15 12:23:35 +053054def repost_actual_qty(item_code, warehouse, allow_zero_rate=False):
Nabin Hait5048c982013-10-23 12:14:32 +053055 try:
Nabin Haitf1a07ff2014-10-15 12:23:35 +053056 update_entries_after({ "item_code": item_code, "warehouse": warehouse }, allow_zero_rate)
Nabin Hait5048c982013-10-23 12:14:32 +053057 except:
58 pass
Rushabh Mehtac4d4c7f2015-10-14 17:37:28 +053059
Nabin Haitb7e46c42015-10-12 16:46:29 +053060def get_balance_qty_from_sle(item_code, warehouse):
61 balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry`
62 where item_code=%s and warehouse=%s and is_cancelled='No'
63 order by posting_date desc, posting_time desc, name desc
64 limit 1""", (item_code, warehouse))
Rushabh Mehtac4d4c7f2015-10-14 17:37:28 +053065
Nabin Haitb7e46c42015-10-12 16:46:29 +053066 return flt(balance_qty[0][0]) if balance_qty else 0.0
Nabin Hait62985362014-04-04 12:05:16 +053067
Nabin Haiteea2b342013-10-11 18:31:33 +053068def get_reserved_qty(item_code, warehouse):
Anand Doshie9baaa62014-02-26 12:35:33 +053069 reserved_qty = frappe.db.sql("""
Nabin Hait62985362014-04-04 12:05:16 +053070 select
Nabin Haiteea2b342013-10-11 18:31:33 +053071 sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty))
Nabin Hait62985362014-04-04 12:05:16 +053072 from
Nabin Haiteea2b342013-10-11 18:31:33 +053073 (
74 (select
75 qty as dnpi_qty,
76 (
77 select qty from `tabSales Order Item`
78 where name = dnpi.parent_detail_docname
Saurabh2e292062015-11-18 17:03:33 +053079 and (delivered_by_supplier is null or delivered_by_supplier = 0)
Nabin Haiteea2b342013-10-11 18:31:33 +053080 ) as so_item_qty,
81 (
Anand Doshi602e8252015-11-16 19:05:46 +053082 select delivered_qty from `tabSales Order Item`
83 where name = dnpi.parent_detail_docname
84 and delivered_by_supplier = 0
Nabin Hait62985362014-04-04 12:05:16 +053085 ) as so_item_delivered_qty,
Nabin Haiteea2b342013-10-11 18:31:33 +053086 parent, name
Nabin Hait62985362014-04-04 12:05:16 +053087 from
Nabin Haiteea2b342013-10-11 18:31:33 +053088 (
89 select qty, parent_detail_docname, parent, name
Nabin Haitd1fd1e22013-10-18 12:29:11 +053090 from `tabPacked Item` dnpi_in
Nabin Haiteea2b342013-10-11 18:31:33 +053091 where item_code = %s and warehouse = %s
92 and parenttype="Sales Order"
Nabin Haitfc2dd442014-10-17 13:05:24 +053093 and item_code != parent_item
Nabin Haiteea2b342013-10-11 18:31:33 +053094 and exists (select * from `tabSales Order` so
Saurabhc6dbe702015-10-19 14:17:52 +053095 where name = dnpi_in.parent and docstatus = 1 and status not in ('Stopped','Closed'))
Nabin Haiteea2b342013-10-11 18:31:33 +053096 ) dnpi)
97 union
98 (select qty as dnpi_qty, qty as so_item_qty,
Anand Doshi602e8252015-11-16 19:05:46 +053099 delivered_qty as so_item_delivered_qty, parent, name
Nabin Haiteea2b342013-10-11 18:31:33 +0530100 from `tabSales Order Item` so_item
Anand Doshi602e8252015-11-16 19:05:46 +0530101 where item_code = %s and warehouse = %s
Saurabh2e292062015-11-18 17:03:33 +0530102 and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
Nabin Haiteea2b342013-10-11 18:31:33 +0530103 and exists(select * from `tabSales Order` so
Nabin Hait62985362014-04-04 12:05:16 +0530104 where so.name = so_item.parent and so.docstatus = 1
Saurabhc6dbe702015-10-19 14:17:52 +0530105 and so.status not in ('Stopped','Closed')))
Nabin Haiteea2b342013-10-11 18:31:33 +0530106 ) tab
Nabin Hait62985362014-04-04 12:05:16 +0530107 where
Nabin Haiteea2b342013-10-11 18:31:33 +0530108 so_item_qty >= so_item_delivered_qty
109 """, (item_code, warehouse, item_code, warehouse))
110
111 return flt(reserved_qty[0][0]) if reserved_qty else 0
Nabin Hait62985362014-04-04 12:05:16 +0530112
Nabin Haiteea2b342013-10-11 18:31:33 +0530113def get_indented_qty(item_code, warehouse):
Anand Doshi602e8252015-11-16 19:05:46 +0530114 indented_qty = frappe.db.sql("""select sum(mr_item.qty - mr_item.ordered_qty)
Nabin Hait4acd4312014-11-04 15:32:31 +0530115 from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
116 where mr_item.item_code=%s and mr_item.warehouse=%s
Anand Doshi602e8252015-11-16 19:05:46 +0530117 and mr_item.qty > mr_item.ordered_qty and mr_item.parent=mr.name
Nabin Hait4acd4312014-11-04 15:32:31 +0530118 and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse))
Nabin Hait62985362014-04-04 12:05:16 +0530119
Nabin Haiteea2b342013-10-11 18:31:33 +0530120 return flt(indented_qty[0][0]) if indented_qty else 0
121
122def get_ordered_qty(item_code, warehouse):
Anand Doshie9baaa62014-02-26 12:35:33 +0530123 ordered_qty = frappe.db.sql("""
Anand Doshi602e8252015-11-16 19:05:46 +0530124 select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor)
Nabin Haiteea2b342013-10-11 18:31:33 +0530125 from `tabPurchase Order Item` po_item, `tabPurchase Order` po
Nabin Hait62985362014-04-04 12:05:16 +0530126 where po_item.item_code=%s and po_item.warehouse=%s
Anand Doshi602e8252015-11-16 19:05:46 +0530127 and po_item.qty > po_item.received_qty and po_item.parent=po.name
Saurabh2e292062015-11-18 17:03:33 +0530128 and po.status not in ('Stopped', 'Closed', 'Delivered') and po.docstatus=1
Anand Doshi602e8252015-11-16 19:05:46 +0530129 and po_item.delivered_by_supplier = 0""", (item_code, warehouse))
Nabin Hait62985362014-04-04 12:05:16 +0530130
Nabin Haiteea2b342013-10-11 18:31:33 +0530131 return flt(ordered_qty[0][0]) if ordered_qty else 0
Nabin Hait62985362014-04-04 12:05:16 +0530132
Nabin Haiteea2b342013-10-11 18:31:33 +0530133def get_planned_qty(item_code, warehouse):
Anand Doshie9baaa62014-02-26 12:35:33 +0530134 planned_qty = frappe.db.sql("""
Anand Doshi602e8252015-11-16 19:05:46 +0530135 select sum(qty - produced_qty) from `tabProduction Order`
Nabin Haiteea2b342013-10-11 18:31:33 +0530136 where production_item = %s and fg_warehouse = %s and status != "Stopped"
Anand Doshi602e8252015-11-16 19:05:46 +0530137 and docstatus=1 and qty > produced_qty""", (item_code, warehouse))
Nabin Haiteea2b342013-10-11 18:31:33 +0530138
139 return flt(planned_qty[0][0]) if planned_qty else 0
Nabin Hait62985362014-04-04 12:05:16 +0530140
141
Nabin Hait7f3f2a02014-09-01 18:16:05 +0530142def update_bin_qty(item_code, warehouse, qty_dict=None):
Rushabh Mehta1f847992013-12-12 19:12:19 +0530143 from erpnext.stock.utils import get_bin
Nabin Haiteea2b342013-10-11 18:31:33 +0530144 bin = get_bin(item_code, warehouse)
145 mismatch = False
146 for fld, val in qty_dict.items():
Anand Doshif78d1ae2014-03-28 13:55:00 +0530147 if flt(bin.get(fld)) != flt(val):
148 bin.set(fld, flt(val))
Nabin Haiteea2b342013-10-11 18:31:33 +0530149 mismatch = True
Nabin Hait62985362014-04-04 12:05:16 +0530150
Nabin Haiteea2b342013-10-11 18:31:33 +0530151 if mismatch:
Anand Doshif78d1ae2014-03-28 13:55:00 +0530152 bin.projected_qty = flt(bin.actual_qty) + flt(bin.ordered_qty) + \
153 flt(bin.indented_qty) + flt(bin.planned_qty) - flt(bin.reserved_qty)
Nabin Hait62985362014-04-04 12:05:16 +0530154
155 bin.save()
156
157def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, posting_time=None,
158 fiscal_year=None):
159 if not posting_date: posting_date = nowdate()
160 if not posting_time: posting_time = nowtime()
161 if not fiscal_year: fiscal_year = get_fiscal_year(posting_date)[0]
162
163 condition = " and item.name='%s'" % item_code.replace("'", "\'") if item_code else ""
164
165 bin = frappe.db.sql("""select bin.item_code, bin.warehouse, bin.actual_qty, item.stock_uom
166 from `tabBin` bin, tabItem item
Rushabh Mehta1e8025b2015-07-24 15:16:25 +0530167 where bin.item_code = item.name and item.has_serial_no = 1 %s""" % condition)
Nabin Hait62985362014-04-04 12:05:16 +0530168
169 for d in bin:
170 serial_nos = frappe.db.sql("""select count(name) from `tabSerial No`
Nabin Hait398c83a2015-10-22 15:11:44 +0530171 where item_code=%s and warehouse=%s and docstatus < 2""", (d[0], d[1]))
Nabin Hait62985362014-04-04 12:05:16 +0530172
173 if serial_nos and flt(serial_nos[0][0]) != flt(d[2]):
174 print d[0], d[1], d[2], serial_nos[0][0]
175
176 sle = frappe.db.sql("""select valuation_rate, company from `tabStock Ledger Entry`
177 where item_code = %s and warehouse = %s and ifnull(is_cancelled, 'No') = 'No'
178 order by posting_date desc limit 1""", (d[0], d[1]))
179
180 sle_dict = {
181 'doctype' : 'Stock Ledger Entry',
182 'item_code' : d[0],
183 'warehouse' : d[1],
184 'transaction_date' : nowdate(),
185 'posting_date' : posting_date,
186 'posting_time' : posting_time,
187 'voucher_type' : 'Stock Reconciliation (Manual)',
188 'voucher_no' : '',
189 'voucher_detail_no' : '',
190 'actual_qty' : flt(serial_nos[0][0]) - flt(d[2]),
191 'stock_uom' : d[3],
192 'incoming_rate' : sle and flt(serial_nos[0][0]) > flt(d[2]) and flt(sle[0][0]) or 0,
193 'company' : sle and cstr(sle[0][1]) or 0,
194 'fiscal_year' : fiscal_year,
195 'is_cancelled' : 'No',
196 'batch_no' : '',
197 'serial_no' : ''
198 }
199
200 sle_doc = frappe.get_doc(sle_dict)
Anand Doshi6dfd4302015-02-10 14:41:27 +0530201 sle_doc.flags.ignore_validate = True
202 sle_doc.flags.ignore_links = True
Nabin Hait62985362014-04-04 12:05:16 +0530203 sle_doc.insert()
204
205 args = sle_dict.copy()
206 args.update({
207 "sle_id": sle_doc.name,
208 "is_amended": 'No'
209 })
210
211 update_bin(args)
212 update_entries_after({
213 "item_code": d[0],
214 "warehouse": d[1],
215 "posting_date": posting_date,
216 "posting_time": posting_time
217 })
nabinhait5c384882014-07-14 11:43:00 +0530218
nabinhait7700c622014-07-14 14:21:21 +0530219def reset_serial_no_status_and_warehouse(serial_nos=None):
nabinhait5c384882014-07-14 11:43:00 +0530220 if not serial_nos:
Nabin Haitc865f222015-09-21 09:18:43 +0530221 serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where docstatus = 0""")
nabinhait5c384882014-07-14 11:43:00 +0530222 for serial_no in serial_nos:
223 try:
224 sr = frappe.get_doc("Serial No", serial_no)
nabinhaitb0a8d002014-07-14 11:56:03 +0530225 last_sle = sr.get_last_sle()
226 if flt(last_sle.actual_qty) > 0:
227 sr.warehouse = last_sle.warehouse
Nabin Hait7f3f2a02014-09-01 18:16:05 +0530228
nabinhait5c384882014-07-14 11:43:00 +0530229 sr.via_stock_ledger = True
230 sr.save()
231 except:
232 pass
Nabin Hait7f3f2a02014-09-01 18:16:05 +0530233
Nabin Hait6c48ef72014-10-08 11:00:38 +0530234def repost_all_stock_vouchers():
Nabin Hait8a28ccf2014-10-14 11:41:44 +0530235 warehouses_with_account = frappe.db.sql_list("""select master_name from tabAccount
236 where ifnull(account_type, '') = 'Warehouse'""")
237
Nabin Hait6c48ef72014-10-08 11:00:38 +0530238 vouchers = frappe.db.sql("""select distinct voucher_type, voucher_no
Nabin Hait8a28ccf2014-10-14 11:41:44 +0530239 from `tabStock Ledger Entry` sle
240 where voucher_type != "Serial No" and sle.warehouse in (%s)
241 order by posting_date, posting_time, name""" %
242 ', '.join(['%s']*len(warehouses_with_account)), tuple(warehouses_with_account))
Nabin Hait6c48ef72014-10-08 11:00:38 +0530243
244 rejected = []
Nabin Hait4d742162014-10-09 19:25:03 +0530245 i = 0
Nabin Hait6c48ef72014-10-08 11:00:38 +0530246 for voucher_type, voucher_no in vouchers:
Nabin Hait4d742162014-10-09 19:25:03 +0530247 i+=1
Nabin Hait7c6f9902014-10-10 18:03:27 +0530248 print i, "/", len(vouchers)
Nabin Hait6c48ef72014-10-08 11:00:38 +0530249 try:
250 for dt in ["Stock Ledger Entry", "GL Entry"]:
251 frappe.db.sql("""delete from `tab%s` where voucher_type=%s and voucher_no=%s"""%
252 (dt, '%s', '%s'), (voucher_type, voucher_no))
253
254 doc = frappe.get_doc(voucher_type, voucher_no)
255 if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]:
Nabin Hait3c3a3ec2015-08-07 17:17:03 +0530256 doc.calculate_rate_and_amount(force=1)
Nabin Hait7c6f9902014-10-10 18:03:27 +0530257 elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes":
258 doc.validate()
Nabin Haite96e83d2014-10-08 18:06:14 +0530259
Nabin Hait6c48ef72014-10-08 11:00:38 +0530260 doc.update_stock_ledger()
Nabin Hait4e7cc932015-01-12 10:55:48 +0530261 doc.make_gl_entries(repost_future_gle=False)
Nabin Haite96e83d2014-10-08 18:06:14 +0530262 frappe.db.commit()
263 except Exception, e:
264 print frappe.get_traceback()
Nabin Hait6c48ef72014-10-08 11:00:38 +0530265 rejected.append([voucher_type, voucher_no])
Nabin Haite96e83d2014-10-08 18:06:14 +0530266 frappe.db.rollback()
Nabin Hait6c48ef72014-10-08 11:00:38 +0530267
268 print rejected