blob: ba03ff2ad75770f5b701d3a38c9110023024f9a6 [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
Chillar Anand915b3432021-09-02 16:44:59 +05304
Rushabh Mehta793ba6b2014-02-14 15:47:51 +05305import frappe
Ankush Menat77be9822022-02-11 11:29:37 +05306from frappe.utils import cstr, flt, now, nowdate, nowtime
Chillar Anand915b3432021-09-02 16:44:59 +05307
Nabin Haita77b8c92020-12-21 14:45:50 +05308from erpnext.controllers.stock_controller import create_repost_item_valuation_entry
Chillar Anand915b3432021-09-02 16:44:59 +05309
Nabin Haiteea2b342013-10-11 18:31:33 +053010
Nabin Haitb7e46c42015-10-12 16:46:29 +053011def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False):
Nabin Haiteea2b342013-10-11 18:31:33 +053012 """
13 Repost everything!
14 """
Anand Doshie9baaa62014-02-26 12:35:33 +053015 frappe.db.auto_commit_on_many_writes = 1
Nabin Hait62985362014-04-04 12:05:16 +053016
Nabin Haitca471f42013-11-20 13:14:12 +053017 if allow_negative_stock:
Ankush Menatbb18ae82024-01-09 21:56:47 +053018 existing_allow_negative_stock = frappe.db.get_single_value(
19 "Stock Settings", "allow_negative_stock"
Ankush Menat494bd9e2022-03-28 18:52:46 +053020 )
Ankush Menata3ea9852023-06-13 17:30:38 +053021 frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
Nabin Hait62985362014-04-04 12:05:16 +053022
Ankush Menat494bd9e2022-03-28 18:52:46 +053023 item_warehouses = frappe.db.sql(
24 """
Nabin Hait001edb42019-09-26 16:44:35 +053025 select distinct item_code, warehouse
26 from
27 (select item_code, warehouse from tabBin
28 union
29 select item_code, warehouse from `tabStock Ledger Entry`) a
Ankush Menat494bd9e2022-03-28 18:52:46 +053030 """
31 )
Nabin Hait001edb42019-09-26 16:44:35 +053032 for d in item_warehouses:
33 try:
34 repost_stock(d[0], d[1], allow_zero_rate, only_actual, only_bin, allow_negative_stock)
35 frappe.db.commit()
Ankush Menat694ae812021-09-01 14:40:56 +053036 except Exception:
Nabin Hait001edb42019-09-26 16:44:35 +053037 frappe.db.rollback()
Nabin Hait62985362014-04-04 12:05:16 +053038
Nabin Haitca471f42013-11-20 13:14:12 +053039 if allow_negative_stock:
Ankush Menata3ea9852023-06-13 17:30:38 +053040 frappe.db.set_single_value(
41 "Stock Settings", "allow_negative_stock", existing_allow_negative_stock
Ankush Menat494bd9e2022-03-28 18:52:46 +053042 )
Anand Doshie9baaa62014-02-26 12:35:33 +053043 frappe.db.auto_commit_on_many_writes = 0
Nabin Haiteea2b342013-10-11 18:31:33 +053044
Ankush Menat494bd9e2022-03-28 18:52:46 +053045
46def repost_stock(
47 item_code,
48 warehouse,
49 allow_zero_rate=False,
50 only_actual=False,
51 only_bin=False,
52 allow_negative_stock=False,
53):
Nabin Hait001edb42019-09-26 16:44:35 +053054
Nabin Haitb7e46c42015-10-12 16:46:29 +053055 if not only_bin:
Nabin Hait001edb42019-09-26 16:44:35 +053056 repost_actual_qty(item_code, warehouse, allow_zero_rate, allow_negative_stock)
Nabin Hait62985362014-04-04 12:05:16 +053057
Nabin Hait2348a5f2014-10-15 15:31:33 +053058 if item_code and warehouse and not only_actual:
Nabin Haitb7e46c42015-10-12 16:46:29 +053059 qty_dict = {
Nabin Haiteea2b342013-10-11 18:31:33 +053060 "reserved_qty": get_reserved_qty(item_code, warehouse),
61 "indented_qty": get_indented_qty(item_code, warehouse),
62 "ordered_qty": get_ordered_qty(item_code, warehouse),
Ankush Menat494bd9e2022-03-28 18:52:46 +053063 "planned_qty": get_planned_qty(item_code, warehouse),
Nabin Haitb7e46c42015-10-12 16:46:29 +053064 }
65 if only_bin:
Ankush Menat494bd9e2022-03-28 18:52:46 +053066 qty_dict.update({"actual_qty": get_balance_qty_from_sle(item_code, warehouse)})
Rushabh Mehtac4d4c7f2015-10-14 17:37:28 +053067
Nabin Haitb7e46c42015-10-12 16:46:29 +053068 update_bin_qty(item_code, warehouse, qty_dict)
Nabin Haiteea2b342013-10-11 18:31:33 +053069
Ankush Menat494bd9e2022-03-28 18:52:46 +053070
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +053071def repost_actual_qty(item_code, warehouse, allow_zero_rate=False, allow_negative_stock=False):
Ankush Menat494bd9e2022-03-28 18:52:46 +053072 create_repost_item_valuation_entry(
73 {
74 "item_code": item_code,
75 "warehouse": warehouse,
76 "posting_date": "1900-01-01",
77 "posting_time": "00:01",
78 "allow_negative_stock": allow_negative_stock,
79 "allow_zero_rate": allow_zero_rate,
80 }
81 )
82
Rushabh Mehtac4d4c7f2015-10-14 17:37:28 +053083
Nabin Haitb7e46c42015-10-12 16:46:29 +053084def get_balance_qty_from_sle(item_code, warehouse):
Ankush Menat494bd9e2022-03-28 18:52:46 +053085 balance_qty = frappe.db.sql(
86 """select qty_after_transaction from `tabStock Ledger Entry`
Nabin Haita77b8c92020-12-21 14:45:50 +053087 where item_code=%s and warehouse=%s and is_cancelled=0
Aditya Hase0c164242019-01-07 22:07:13 +053088 order by posting_date desc, posting_time desc, creation desc
Ankush Menat494bd9e2022-03-28 18:52:46 +053089 limit 1""",
90 (item_code, warehouse),
91 )
Rushabh Mehtac4d4c7f2015-10-14 17:37:28 +053092
Nabin Haitb7e46c42015-10-12 16:46:29 +053093 return flt(balance_qty[0][0]) if balance_qty else 0.0
Nabin Hait62985362014-04-04 12:05:16 +053094
Ankush Menat494bd9e2022-03-28 18:52:46 +053095
Nabin Haiteea2b342013-10-11 18:31:33 +053096def get_reserved_qty(item_code, warehouse):
Devin Slauenwhite3c553b02023-03-27 10:24:15 -040097 dont_reserve_on_return = frappe.get_cached_value(
98 "Selling Settings", "Selling Settings", "dont_reserve_sales_order_qty_on_sales_return"
99 )
Devin Slauenwhite17f80802023-03-25 00:24:53 -0400100 reserved_qty = frappe.db.sql(
Devin Slauenwhite3c553b02023-03-27 10:24:15 -0400101 f"""
Devin Slauenwhite17f80802023-03-25 00:24:53 -0400102 select
Devin Slauenwhite3c553b02023-03-27 10:24:15 -0400103 sum(dnpi_qty * ((so_item_qty - so_item_delivered_qty - if(dont_reserve_qty_on_return, so_item_returned_qty, 0)) / so_item_qty))
Devin Slauenwhite17f80802023-03-25 00:24:53 -0400104 from
105 (
106 (select
107 qty as dnpi_qty,
Nabin Haiteea2b342013-10-11 18:31:33 +0530108 (
Devin Slauenwhite17f80802023-03-25 00:24:53 -0400109 select qty from `tabSales Order Item`
110 where name = dnpi.parent_detail_docname
111 and (delivered_by_supplier is null or delivered_by_supplier = 0)
112 ) as so_item_qty,
113 (
114 select delivered_qty from `tabSales Order Item`
115 where name = dnpi.parent_detail_docname
116 and delivered_by_supplier = 0
117 ) as so_item_delivered_qty,
Devin Slauenwhite3c553b02023-03-27 10:24:15 -0400118 (
119 select returned_qty from `tabSales Order Item`
120 where name = dnpi.parent_detail_docname
121 and delivered_by_supplier = 0
122 ) as so_item_returned_qty,
123 {dont_reserve_on_return} as dont_reserve_qty_on_return,
Devin Slauenwhite17f80802023-03-25 00:24:53 -0400124 parent, name
125 from
126 (
127 select qty, parent_detail_docname, parent, name
128 from `tabPacked Item` dnpi_in
129 where item_code = %s and warehouse = %s
130 and parenttype='Sales Order'
131 and item_code != parent_item
132 and exists (select * from `tabSales Order` so
133 where name = dnpi_in.parent and docstatus = 1 and status not in ('On Hold', 'Closed'))
134 ) dnpi)
135 union
136 (select stock_qty as dnpi_qty, qty as so_item_qty,
Devin Slauenwhite3c553b02023-03-27 10:24:15 -0400137 delivered_qty as so_item_delivered_qty,
138 returned_qty as so_item_returned_qty,
139 {dont_reserve_on_return}, parent, name
Devin Slauenwhite17f80802023-03-25 00:24:53 -0400140 from `tabSales Order Item` so_item
141 where item_code = %s and warehouse = %s
142 and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
143 and exists(select * from `tabSales Order` so
144 where so.name = so_item.parent and so.docstatus = 1
145 and so.status not in ('On Hold', 'Closed')))
146 ) tab
147 where
148 so_item_qty >= so_item_delivered_qty
149 """,
150 (item_code, warehouse, item_code, warehouse),
Devin Slauenwhite03288742022-06-17 14:45:33 -0400151 )
Nabin Haiteea2b342013-10-11 18:31:33 +0530152
153 return flt(reserved_qty[0][0]) if reserved_qty else 0
Nabin Hait62985362014-04-04 12:05:16 +0530154
Ankush Menat494bd9e2022-03-28 18:52:46 +0530155
Nabin Haiteea2b342013-10-11 18:31:33 +0530156def get_indented_qty(item_code, warehouse):
Nabin Hait7eaa7f22020-04-17 10:52:28 +0530157 # Ordered Qty is always maintained in stock UOM
Ankush Menat494bd9e2022-03-28 18:52:46 +0530158 inward_qty = frappe.db.sql(
159 """
Nabin Hait03af0292020-04-16 20:11:32 +0530160 select sum(mr_item.stock_qty - mr_item.ordered_qty)
Nabin Hait4acd4312014-11-04 15:32:31 +0530161 from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
162 where mr_item.item_code=%s and mr_item.warehouse=%s
Nabin Hait7eaa7f22020-04-17 10:52:28 +0530163 and mr.material_request_type in ('Purchase', 'Manufacture', 'Customer Provided', 'Material Transfer')
Nabin Hait03af0292020-04-16 20:11:32 +0530164 and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name
165 and mr.status!='Stopped' and mr.docstatus=1
Ankush Menat494bd9e2022-03-28 18:52:46 +0530166 """,
167 (item_code, warehouse),
168 )
Nabin Hait7eaa7f22020-04-17 10:52:28 +0530169 inward_qty = flt(inward_qty[0][0]) if inward_qty else 0
Nabin Hait62985362014-04-04 12:05:16 +0530170
Ankush Menat494bd9e2022-03-28 18:52:46 +0530171 outward_qty = frappe.db.sql(
172 """
Nabin Hait03af0292020-04-16 20:11:32 +0530173 select sum(mr_item.stock_qty - mr_item.ordered_qty)
174 from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
175 where mr_item.item_code=%s and mr_item.warehouse=%s
Nabin Hait7eaa7f22020-04-17 10:52:28 +0530176 and mr.material_request_type = 'Material Issue'
Nabin Hait03af0292020-04-16 20:11:32 +0530177 and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name
178 and mr.status!='Stopped' and mr.docstatus=1
Ankush Menat494bd9e2022-03-28 18:52:46 +0530179 """,
180 (item_code, warehouse),
181 )
Nabin Hait7eaa7f22020-04-17 10:52:28 +0530182 outward_qty = flt(outward_qty[0][0]) if outward_qty else 0
marination815c36a2020-03-26 14:49:28 +0530183
Nabin Hait7eaa7f22020-04-17 10:52:28 +0530184 requested_qty = inward_qty - outward_qty
Nabin Hait03af0292020-04-16 20:11:32 +0530185
186 return requested_qty
Nabin Haiteea2b342013-10-11 18:31:33 +0530187
Ankush Menat494bd9e2022-03-28 18:52:46 +0530188
Nabin Haiteea2b342013-10-11 18:31:33 +0530189def get_ordered_qty(item_code, warehouse):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530190 ordered_qty = frappe.db.sql(
191 """
Anand Doshi602e8252015-11-16 19:05:46 +0530192 select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor)
Nabin Haiteea2b342013-10-11 18:31:33 +0530193 from `tabPurchase Order Item` po_item, `tabPurchase Order` po
Nabin Hait62985362014-04-04 12:05:16 +0530194 where po_item.item_code=%s and po_item.warehouse=%s
Anand Doshi602e8252015-11-16 19:05:46 +0530195 and po_item.qty > po_item.received_qty and po_item.parent=po.name
patilsangrambf2b5112016-02-22 16:24:23 +0530196 and po.status not in ('Closed', 'Delivered') and po.docstatus=1
Ankush Menat494bd9e2022-03-28 18:52:46 +0530197 and po_item.delivered_by_supplier = 0""",
198 (item_code, warehouse),
199 )
Nabin Hait62985362014-04-04 12:05:16 +0530200
Nabin Haiteea2b342013-10-11 18:31:33 +0530201 return flt(ordered_qty[0][0]) if ordered_qty else 0
Nabin Hait62985362014-04-04 12:05:16 +0530202
Ankush Menat494bd9e2022-03-28 18:52:46 +0530203
Nabin Haiteea2b342013-10-11 18:31:33 +0530204def get_planned_qty(item_code, warehouse):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530205 planned_qty = frappe.db.sql(
206 """
Zarrar13ddc7e2018-03-20 12:38:43 +0530207 select sum(qty - produced_qty) from `tabWork Order`
Conor74a782d2022-06-17 06:31:27 -0500208 where production_item = %s and fg_warehouse = %s and status not in ('Stopped', 'Completed', 'Closed')
Ankush Menat494bd9e2022-03-28 18:52:46 +0530209 and docstatus=1 and qty > produced_qty""",
210 (item_code, warehouse),
211 )
Nabin Haiteea2b342013-10-11 18:31:33 +0530212
213 return flt(planned_qty[0][0]) if planned_qty else 0
Nabin Hait62985362014-04-04 12:05:16 +0530214
215
Nabin Hait7f3f2a02014-09-01 18:16:05 +0530216def update_bin_qty(item_code, warehouse, qty_dict=None):
Rushabh Mehta1f847992013-12-12 19:12:19 +0530217 from erpnext.stock.utils import get_bin
Ankush Menat494bd9e2022-03-28 18:52:46 +0530218
Nabin Haiteea2b342013-10-11 18:31:33 +0530219 bin = get_bin(item_code, warehouse)
220 mismatch = False
marination815c36a2020-03-26 14:49:28 +0530221 for field, value in qty_dict.items():
222 if flt(bin.get(field)) != flt(value):
223 bin.set(field, flt(value))
Nabin Haiteea2b342013-10-11 18:31:33 +0530224 mismatch = True
Nabin Hait62985362014-04-04 12:05:16 +0530225
Ankush Menat77be9822022-02-11 11:29:37 +0530226 bin.modified = now()
Nabin Haiteea2b342013-10-11 18:31:33 +0530227 if mismatch:
Himanshu Mishraf56aac62018-08-08 18:32:03 +0530228 bin.set_projected_qty()
229 bin.db_update()
230 bin.clear_cache()
Nabin Hait62985362014-04-04 12:05:16 +0530231
Nabin Hait62985362014-04-04 12:05:16 +0530232
Ankush Menat494bd9e2022-03-28 18:52:46 +0530233def set_stock_balance_as_per_serial_no(
234 item_code=None, posting_date=None, posting_time=None, fiscal_year=None
235):
236 if not posting_date:
237 posting_date = nowdate()
238 if not posting_time:
239 posting_time = nowtime()
Nabin Hait62985362014-04-04 12:05:16 +0530240
Ankush Menat494bd9e2022-03-28 18:52:46 +0530241 condition = " and item.name='%s'" % item_code.replace("'", "'") if item_code else ""
242
243 bin = frappe.db.sql(
244 """select bin.item_code, bin.warehouse, bin.actual_qty, item.stock_uom
Nabin Hait62985362014-04-04 12:05:16 +0530245 from `tabBin` bin, tabItem item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530246 where bin.item_code = item.name and item.has_serial_no = 1 %s"""
247 % condition
248 )
Nabin Hait62985362014-04-04 12:05:16 +0530249
250 for d in bin:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530251 serial_nos = frappe.db.sql(
252 """select count(name) from `tabSerial No`
253 where item_code=%s and warehouse=%s and docstatus < 2""",
254 (d[0], d[1]),
255 )
Nabin Hait62985362014-04-04 12:05:16 +0530256
Ankush Menat494bd9e2022-03-28 18:52:46 +0530257 sle = frappe.db.sql(
258 """select valuation_rate, company from `tabStock Ledger Entry`
Nabin Haita77b8c92020-12-21 14:45:50 +0530259 where item_code = %s and warehouse = %s and is_cancelled = 0
Ankush Menat494bd9e2022-03-28 18:52:46 +0530260 order by posting_date desc limit 1""",
261 (d[0], d[1]),
262 )
Nabin Hait62985362014-04-04 12:05:16 +0530263
264 sle_dict = {
Ankush Menat494bd9e2022-03-28 18:52:46 +0530265 "doctype": "Stock Ledger Entry",
266 "item_code": d[0],
267 "warehouse": d[1],
268 "transaction_date": nowdate(),
269 "posting_date": posting_date,
270 "posting_time": posting_time,
271 "voucher_type": "Stock Reconciliation (Manual)",
272 "voucher_no": "",
273 "voucher_detail_no": "",
274 "actual_qty": flt(serial_nos[0][0]) - flt(d[2]),
275 "stock_uom": d[3],
276 "incoming_rate": sle and flt(serial_nos[0][0]) > flt(d[2]) and flt(sle[0][0]) or 0,
277 "company": sle and cstr(sle[0][1]) or 0,
278 "batch_no": "",
279 "serial_no": "",
Nabin Hait62985362014-04-04 12:05:16 +0530280 }
281
282 sle_doc = frappe.get_doc(sle_dict)
Anand Doshi6dfd4302015-02-10 14:41:27 +0530283 sle_doc.flags.ignore_validate = True
284 sle_doc.flags.ignore_links = True
Nabin Hait62985362014-04-04 12:05:16 +0530285 sle_doc.insert()
286
287 args = sle_dict.copy()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530288 args.update({"sle_id": sle_doc.name})
Nabin Hait62985362014-04-04 12:05:16 +0530289
Ankush Menat494bd9e2022-03-28 18:52:46 +0530290 create_repost_item_valuation_entry(
291 {
292 "item_code": d[0],
293 "warehouse": d[1],
294 "posting_date": posting_date,
295 "posting_time": posting_time,
296 }
297 )