Rushabh Mehta | ad45e31 | 2013-11-20 12:59:58 +0530 | [diff] [blame] | 1 | # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 2 | # License: GNU General Public License v3. See license.txt |
| 3 | |
| 4 | from __future__ import unicode_literals |
Rushabh Mehta | 793ba6b | 2014-02-14 15:47:51 +0530 | [diff] [blame] | 5 | import frappe |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 6 | |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 7 | from frappe.utils import flt, cstr, nowdate, nowtime |
| 8 | from erpnext.stock.utils import update_bin |
| 9 | from erpnext.stock.stock_ledger import update_entries_after |
| 10 | from erpnext.accounts.utils import get_fiscal_year |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 11 | |
Nabin Hait | ca471f4 | 2013-11-20 13:14:12 +0530 | [diff] [blame] | 12 | def repost(allow_negative_stock=False): |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 13 | """ |
| 14 | Repost everything! |
| 15 | """ |
Anand Doshi | e9baaa6 | 2014-02-26 12:35:33 +0530 | [diff] [blame] | 16 | frappe.db.auto_commit_on_many_writes = 1 |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 17 | |
Nabin Hait | ca471f4 | 2013-11-20 13:14:12 +0530 | [diff] [blame] | 18 | if allow_negative_stock: |
Anand Doshi | e9baaa6 | 2014-02-26 12:35:33 +0530 | [diff] [blame] | 19 | frappe.db.set_default("allow_negative_stock", 1) |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 20 | |
| 21 | for d in frappe.db.sql("""select distinct item_code, warehouse from |
Nabin Hait | 4ed7f68 | 2013-10-23 11:50:09 +0530 | [diff] [blame] | 22 | (select item_code, warehouse from tabBin |
| 23 | union |
Nabin Hait | c2caae5 | 2013-10-23 12:02:08 +0530 | [diff] [blame] | 24 | select item_code, warehouse from `tabStock Ledger Entry`) a"""): |
Nabin Hait | ca471f4 | 2013-11-20 13:14:12 +0530 | [diff] [blame] | 25 | repost_stock(d[0], d[1], allow_negative_stock) |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 26 | |
Nabin Hait | ca471f4 | 2013-11-20 13:14:12 +0530 | [diff] [blame] | 27 | if allow_negative_stock: |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 28 | frappe.db.set_default("allow_negative_stock", |
Anand Doshi | e9baaa6 | 2014-02-26 12:35:33 +0530 | [diff] [blame] | 29 | frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) |
| 30 | frappe.db.auto_commit_on_many_writes = 0 |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 31 | |
| 32 | def repost_stock(item_code, warehouse): |
| 33 | repost_actual_qty(item_code, warehouse) |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 34 | |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 35 | if item_code and warehouse: |
| 36 | update_bin(item_code, warehouse, { |
| 37 | "reserved_qty": get_reserved_qty(item_code, warehouse), |
| 38 | "indented_qty": get_indented_qty(item_code, warehouse), |
| 39 | "ordered_qty": get_ordered_qty(item_code, warehouse), |
| 40 | "planned_qty": get_planned_qty(item_code, warehouse) |
| 41 | }) |
| 42 | |
| 43 | def repost_actual_qty(item_code, warehouse): |
Nabin Hait | 5048c98 | 2013-10-23 12:14:32 +0530 | [diff] [blame] | 44 | try: |
| 45 | update_entries_after({ "item_code": item_code, "warehouse": warehouse }) |
| 46 | except: |
| 47 | pass |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 48 | |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 49 | def get_reserved_qty(item_code, warehouse): |
Anand Doshi | e9baaa6 | 2014-02-26 12:35:33 +0530 | [diff] [blame] | 50 | reserved_qty = frappe.db.sql(""" |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 51 | select |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 52 | sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty)) |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 53 | from |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 54 | ( |
| 55 | (select |
| 56 | qty as dnpi_qty, |
| 57 | ( |
| 58 | select qty from `tabSales Order Item` |
| 59 | where name = dnpi.parent_detail_docname |
| 60 | ) as so_item_qty, |
| 61 | ( |
| 62 | select ifnull(delivered_qty, 0) from `tabSales Order Item` |
| 63 | where name = dnpi.parent_detail_docname |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 64 | ) as so_item_delivered_qty, |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 65 | parent, name |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 66 | from |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 67 | ( |
| 68 | select qty, parent_detail_docname, parent, name |
Nabin Hait | d1fd1e2 | 2013-10-18 12:29:11 +0530 | [diff] [blame] | 69 | from `tabPacked Item` dnpi_in |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 70 | where item_code = %s and warehouse = %s |
| 71 | and parenttype="Sales Order" |
| 72 | and item_code != parent_item |
| 73 | and exists (select * from `tabSales Order` so |
| 74 | where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped') |
| 75 | ) dnpi) |
| 76 | union |
| 77 | (select qty as dnpi_qty, qty as so_item_qty, |
| 78 | ifnull(delivered_qty, 0) as so_item_delivered_qty, parent, name |
| 79 | from `tabSales Order Item` so_item |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 80 | where item_code = %s and reserved_warehouse = %s |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 81 | and exists(select * from `tabSales Order` so |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 82 | where so.name = so_item.parent and so.docstatus = 1 |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 83 | and so.status != 'Stopped')) |
| 84 | ) tab |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 85 | where |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 86 | so_item_qty >= so_item_delivered_qty |
| 87 | """, (item_code, warehouse, item_code, warehouse)) |
| 88 | |
| 89 | return flt(reserved_qty[0][0]) if reserved_qty else 0 |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 90 | |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 91 | def get_indented_qty(item_code, warehouse): |
Anand Doshi | e9baaa6 | 2014-02-26 12:35:33 +0530 | [diff] [blame] | 92 | indented_qty = frappe.db.sql("""select sum(pr_item.qty - ifnull(pr_item.ordered_qty, 0)) |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 93 | from `tabMaterial Request Item` pr_item, `tabMaterial Request` pr |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 94 | where pr_item.item_code=%s and pr_item.warehouse=%s |
| 95 | and pr_item.qty > ifnull(pr_item.ordered_qty, 0) and pr_item.parent=pr.name |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 96 | and pr.status!='Stopped' and pr.docstatus=1""", (item_code, warehouse)) |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 97 | |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 98 | return flt(indented_qty[0][0]) if indented_qty else 0 |
| 99 | |
| 100 | def get_ordered_qty(item_code, warehouse): |
Anand Doshi | e9baaa6 | 2014-02-26 12:35:33 +0530 | [diff] [blame] | 101 | ordered_qty = frappe.db.sql(""" |
Nabin Hait | 1a60fd8 | 2013-10-14 14:27:08 +0530 | [diff] [blame] | 102 | select sum((po_item.qty - ifnull(po_item.received_qty, 0))*po_item.conversion_factor) |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 103 | from `tabPurchase Order Item` po_item, `tabPurchase Order` po |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 104 | where po_item.item_code=%s and po_item.warehouse=%s |
| 105 | and po_item.qty > ifnull(po_item.received_qty, 0) and po_item.parent=po.name |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 106 | and po.status!='Stopped' and po.docstatus=1""", (item_code, warehouse)) |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 107 | |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 108 | return flt(ordered_qty[0][0]) if ordered_qty else 0 |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 109 | |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 110 | def get_planned_qty(item_code, warehouse): |
Anand Doshi | e9baaa6 | 2014-02-26 12:35:33 +0530 | [diff] [blame] | 111 | planned_qty = frappe.db.sql(""" |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 112 | select sum(ifnull(qty, 0) - ifnull(produced_qty, 0)) from `tabProduction Order` |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 113 | where production_item = %s and fg_warehouse = %s and status != "Stopped" |
Nabin Hait | 1a60fd8 | 2013-10-14 14:27:08 +0530 | [diff] [blame] | 114 | and docstatus=1 and ifnull(qty, 0) > ifnull(produced_qty, 0)""", (item_code, warehouse)) |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 115 | |
| 116 | return flt(planned_qty[0][0]) if planned_qty else 0 |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 117 | |
| 118 | |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 119 | def update_bin(item_code, warehouse, qty_dict=None): |
Rushabh Mehta | 1f84799 | 2013-12-12 19:12:19 +0530 | [diff] [blame] | 120 | from erpnext.stock.utils import get_bin |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 121 | bin = get_bin(item_code, warehouse) |
| 122 | mismatch = False |
| 123 | for fld, val in qty_dict.items(): |
Anand Doshi | f78d1ae | 2014-03-28 13:55:00 +0530 | [diff] [blame] | 124 | if flt(bin.get(fld)) != flt(val): |
| 125 | bin.set(fld, flt(val)) |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 126 | mismatch = True |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 127 | |
Nabin Hait | eea2b34 | 2013-10-11 18:31:33 +0530 | [diff] [blame] | 128 | if mismatch: |
Anand Doshi | f78d1ae | 2014-03-28 13:55:00 +0530 | [diff] [blame] | 129 | bin.projected_qty = flt(bin.actual_qty) + flt(bin.ordered_qty) + \ |
| 130 | flt(bin.indented_qty) + flt(bin.planned_qty) - flt(bin.reserved_qty) |
Nabin Hait | 6298536 | 2014-04-04 12:05:16 +0530 | [diff] [blame] | 131 | |
| 132 | bin.save() |
| 133 | |
| 134 | def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, posting_time=None, |
| 135 | fiscal_year=None): |
| 136 | if not posting_date: posting_date = nowdate() |
| 137 | if not posting_time: posting_time = nowtime() |
| 138 | if not fiscal_year: fiscal_year = get_fiscal_year(posting_date)[0] |
| 139 | |
| 140 | condition = " and item.name='%s'" % item_code.replace("'", "\'") if item_code else "" |
| 141 | |
| 142 | bin = frappe.db.sql("""select bin.item_code, bin.warehouse, bin.actual_qty, item.stock_uom |
| 143 | from `tabBin` bin, tabItem item |
| 144 | where bin.item_code = item.name and item.has_serial_no = 'Yes' %s""" % condition) |
| 145 | |
| 146 | for d in bin: |
| 147 | serial_nos = frappe.db.sql("""select count(name) from `tabSerial No` |
| 148 | where item_code=%s and warehouse=%s and status = 'Available' and docstatus < 2""", (d[0], d[1])) |
| 149 | |
| 150 | if serial_nos and flt(serial_nos[0][0]) != flt(d[2]): |
| 151 | print d[0], d[1], d[2], serial_nos[0][0] |
| 152 | |
| 153 | sle = frappe.db.sql("""select valuation_rate, company from `tabStock Ledger Entry` |
| 154 | where item_code = %s and warehouse = %s and ifnull(is_cancelled, 'No') = 'No' |
| 155 | order by posting_date desc limit 1""", (d[0], d[1])) |
| 156 | |
| 157 | sle_dict = { |
| 158 | 'doctype' : 'Stock Ledger Entry', |
| 159 | 'item_code' : d[0], |
| 160 | 'warehouse' : d[1], |
| 161 | 'transaction_date' : nowdate(), |
| 162 | 'posting_date' : posting_date, |
| 163 | 'posting_time' : posting_time, |
| 164 | 'voucher_type' : 'Stock Reconciliation (Manual)', |
| 165 | 'voucher_no' : '', |
| 166 | 'voucher_detail_no' : '', |
| 167 | 'actual_qty' : flt(serial_nos[0][0]) - flt(d[2]), |
| 168 | 'stock_uom' : d[3], |
| 169 | 'incoming_rate' : sle and flt(serial_nos[0][0]) > flt(d[2]) and flt(sle[0][0]) or 0, |
| 170 | 'company' : sle and cstr(sle[0][1]) or 0, |
| 171 | 'fiscal_year' : fiscal_year, |
| 172 | 'is_cancelled' : 'No', |
| 173 | 'batch_no' : '', |
| 174 | 'serial_no' : '' |
| 175 | } |
| 176 | |
| 177 | sle_doc = frappe.get_doc(sle_dict) |
| 178 | sle_doc.insert() |
| 179 | |
| 180 | args = sle_dict.copy() |
| 181 | args.update({ |
| 182 | "sle_id": sle_doc.name, |
| 183 | "is_amended": 'No' |
| 184 | }) |
| 185 | |
| 186 | update_bin(args) |
| 187 | update_entries_after({ |
| 188 | "item_code": d[0], |
| 189 | "warehouse": d[1], |
| 190 | "posting_date": posting_date, |
| 191 | "posting_time": posting_time |
| 192 | }) |