From 4d742161472ac5228d100c7aab4994c8fc40fdaa Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 9 Oct 2014 19:25:03 +0530 Subject: [PATCH 1/5] Maintain negative stock balance if balance qty is negative --- erpnext/controllers/buying_controller.py | 3 +- erpnext/controllers/stock_controller.py | 8 ++- .../doctype/stock_entry/test_stock_entry.py | 66 +++++++++++++++--- .../test_stock_reconciliation.py | 12 ++-- .../stock/page/stock_balance/stock_balance.js | 1 + erpnext/stock/stock_ledger.py | 68 +++++++++++-------- erpnext/stock/utils.py | 3 +- erpnext/utilities/repost_stock.py | 7 +- 8 files changed, 118 insertions(+), 50 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 3d25c213ed..66d5792e2d 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -269,7 +269,7 @@ class BuyingController(StockController): # get raw materials rate if self.doctype == "Purchase Receipt": from erpnext.stock.utils import get_incoming_rate - rm.rate = get_incoming_rate({ + item_rate = get_incoming_rate({ "item_code": bom_item.item_code, "warehouse": self.supplier_warehouse, "posting_date": self.posting_date, @@ -277,6 +277,7 @@ class BuyingController(StockController): "qty": -1 * required_qty, "serial_no": rm.serial_no }) + rm.rate = item_rate or bom_item.rate else: rm.rate = bom_item.rate diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 20eb40c418..ad553d8c83 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -305,9 +305,15 @@ def get_valuation_rate(item_code, warehouse): last_valuation_rate = frappe.db.sql("""select valuation_rate from `tabStock Ledger Entry` where item_code = %s and warehouse = %s - and ifnull(qty_after_transaction, 0) > 0 + and ifnull(valuation_rate, 0) > 0 order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse)) + if not last_valuation_rate: + last_valuation_rate = frappe.db.sql("""select valuation_rate + from `tabStock Ledger Entry` + where item_code = %s and ifnull(valuation_rate, 0) > 0 + order by posting_date desc, posting_time desc, name desc limit 1""", item_code) + valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0 if not valuation_rate: diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 038606cc1d..7654cbcccd 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -9,14 +9,64 @@ from erpnext.stock.doctype.serial_no.serial_no import * from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError +def get_sle(**args): + condition, values = "", [] + for key, value in args.iteritems(): + condition += " and " if condition else " where " + condition += "`{0}`=%s".format(key) + values.append(value) + + return frappe.db.sql("""select * from `tabStock Ledger Entry` %s + order by timestamp(posting_date, posting_time) desc, name desc limit 1"""% condition, + values, as_dict=1) + +def make_zero(item_code, warehouse): + sle = get_sle(item_code = item_code, warehouse = warehouse) + qty = sle[0].qty_after_transaction if sle else 0 + if qty < 0: + make_stock_entry(item_code, None, warehouse, abs(qty), incoming_rate=10) + elif qty > 0: + make_stock_entry(item_code, warehouse, None, qty, incoming_rate=10) + class TestStockEntry(unittest.TestCase): - def tearDown(self): frappe.set_user("Administrator") set_perpetual_inventory(0) if hasattr(self, "old_default_company"): frappe.db.set_default("company", self.old_default_company) + def test_fifo(self): + frappe.db.set_default("allow_negative_stock", 1) + item_code = "_Test Item 2" + warehouse = "_Test Warehouse - _TC" + make_zero(item_code, warehouse) + + make_stock_entry(item_code, None, warehouse, 1, incoming_rate=10) + sle = get_sle(item_code = item_code, warehouse = warehouse)[0] + + self.assertEqual([[1, 10]], eval(sle.stock_queue)) + + # negative qty + make_zero(item_code, warehouse) + make_stock_entry(item_code, warehouse, None, 1, incoming_rate=10) + sle = get_sle(item_code = item_code, warehouse = warehouse)[0] + + self.assertEqual([[-1, 10]], eval(sle.stock_queue)) + + # further negative + make_stock_entry(item_code, warehouse, None, 1) + sle = get_sle(item_code = item_code, warehouse = warehouse)[0] + + self.assertEqual([[-2, 10]], eval(sle.stock_queue)) + + # move stock to positive + make_stock_entry(item_code, None, warehouse, 3, incoming_rate=10) + sle = get_sle(item_code = item_code, warehouse = warehouse)[0] + + self.assertEqual([[1, 10]], eval(sle.stock_queue)) + + frappe.db.set_default("allow_negative_stock", 0) + def test_auto_material_request(self): frappe.db.sql("""delete from `tabMaterial Request Item`""") frappe.db.sql("""delete from `tabMaterial Request`""") @@ -821,19 +871,19 @@ class TestStockEntry(unittest.TestCase): se = frappe.copy_doc(test_records[0]).insert() self.assertRaises (StockFreezeError, se.submit) frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0) - + def test_production_order(self): - bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", + bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", "is_default": 1, "docstatus": 1}) - + production_order = frappe.new_doc("Production Order") production_order.update({ "company": "_Test Company", - "fg_warehouse": "_Test Warehouse 1 - _TC", - "production_item": "_Test FG Item 2", + "fg_warehouse": "_Test Warehouse 1 - _TC", + "production_item": "_Test FG Item 2", "bom_no": bom_no, "qty": 1.0, - "stock_uom": "Nos", + "stock_uom": "Nos", "wip_warehouse": "_Test Warehouse - _TC" }) production_order.insert() @@ -889,5 +939,3 @@ def make_stock_entry(item, source, target, qty, incoming_rate=None): s.insert() s.submit() return s - -test_records = frappe.get_test_records('Stock Entry') diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 183e3e51d2..91775d9087 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -28,7 +28,7 @@ class TestStockReconciliation(unittest.TestCase): [20, "", "2012-12-26", "12:05", 16000, 15, 18000], [10, 2000, "2012-12-26", "12:10", 20000, 5, 6000], [1, 1000, "2012-12-01", "00:00", 1000, 11, 13200], - [0, "", "2012-12-26", "12:10", 0, -5, 0] + [0, "", "2012-12-26", "12:10", 0, -5, -6000] ] for d in input_data: @@ -63,16 +63,16 @@ class TestStockReconciliation(unittest.TestCase): input_data = [ [50, 1000, "2012-12-26", "12:00", 50000, 45, 48000], [5, 1000, "2012-12-26", "12:00", 5000, 0, 0], - [15, 1000, "2012-12-26", "12:00", 15000, 10, 12000], + [15, 1000, "2012-12-26", "12:00", 15000, 10, 11500], [25, 900, "2012-12-26", "12:00", 22500, 20, 22500], [20, 500, "2012-12-26", "12:00", 10000, 15, 18000], [50, 1000, "2013-01-01", "12:00", 50000, 65, 68000], [5, 1000, "2013-01-01", "12:00", 5000, 20, 23000], - ["", 1000, "2012-12-26", "12:05", 15000, 10, 12000], + ["", 1000, "2012-12-26", "12:05", 15000, 10, 11500], [20, "", "2012-12-26", "12:05", 18000, 15, 18000], - [10, 2000, "2012-12-26", "12:10", 20000, 5, 6000], - [1, 1000, "2012-12-01", "00:00", 1000, 11, 13200], - [0, "", "2012-12-26", "12:10", 0, -5, 0] + [10, 2000, "2012-12-26", "12:10", 20000, 5, 7600], + [1, 1000, "2012-12-01", "00:00", 1000, 11, 12512.73], + [0, "", "2012-12-26", "12:10", 0, -5, -5142.86] ] diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js index ecd1108bc6..198d317824 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ b/erpnext/stock/page/stock_balance/stock_balance.js @@ -126,6 +126,7 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({ && this.stock_entry_map[sl.voucher_no].purpose=="Material Transfer"; if(!ignore_inflow_outflow) { + if(qty_diff < 0) { item.outflow_qty += Math.abs(qty_diff); } else { diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index e8a84c2ab1..f1ba94efab 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -106,18 +106,19 @@ def update_entries_after(args, verbose=1): stock_queue = [[qty_after_transaction, valuation_rate]] else: if valuation_method == "Moving Average": - valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate) + if flt(sle.actual_qty) > 0: + valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate) else: valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue) + qty_after_transaction += flt(sle.actual_qty) # get stock value if sle.serial_no: stock_value = qty_after_transaction * valuation_rate elif valuation_method == "Moving Average": - stock_value = (qty_after_transaction > 0) and \ - (qty_after_transaction * valuation_rate) or 0 + stock_value = qty_after_transaction * valuation_rate else: stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue)) @@ -256,64 +257,73 @@ def get_moving_average_values(qty_after_transaction, sle, valuation_rate): actual_qty = flt(sle.actual_qty) if not incoming_rate: - # In case of delivery/stock issue in_rate = 0 or wrong incoming rate + # If wrong incoming rate incoming_rate = valuation_rate - elif qty_after_transaction < 0: + elif qty_after_transaction < 0 and not valuation_rate: # if negative stock, take current valuation rate as incoming rate valuation_rate = incoming_rate - new_stock_qty = qty_after_transaction + actual_qty - new_stock_value = qty_after_transaction * valuation_rate + actual_qty * incoming_rate + new_stock_qty = abs(qty_after_transaction) + actual_qty + new_stock_value = (abs(qty_after_transaction) * valuation_rate) + (actual_qty * incoming_rate) - if new_stock_qty > 0 and new_stock_value > 0: + if new_stock_qty: valuation_rate = new_stock_value / flt(new_stock_qty) - elif new_stock_qty <= 0: - valuation_rate = 0.0 - # NOTE: val_rate is same as previous entry if new stock value is negative - - return valuation_rate + return abs(valuation_rate) def get_fifo_values(qty_after_transaction, sle, stock_queue): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) - if not stock_queue: - stock_queue.append([0, 0]) + + intialize_stock_queue(stock_queue, sle.item_code, sle.warehouse) if actual_qty > 0: if stock_queue[-1][0] > 0: stock_queue.append([actual_qty, incoming_rate]) else: qty = stock_queue[-1][0] + actual_qty - stock_queue[-1] = [qty, qty > 0 and incoming_rate or 0] + if qty == 0: + stock_queue.pop(-1) + else: + stock_queue[-1] = [qty, incoming_rate] else: - incoming_cost = 0 qty_to_pop = abs(actual_qty) while qty_to_pop: - if not stock_queue: - stock_queue.append([0, 0]) + intialize_stock_queue(stock_queue, sle.item_code, sle.warehouse) batch = stock_queue[0] - if 0 < batch[0] <= qty_to_pop: - # if batch qty > 0 - # not enough or exactly same qty in current batch, clear batch - incoming_cost += flt(batch[0]) * flt(batch[1]) - qty_to_pop -= batch[0] + # print qty_to_pop, batch + + if qty_to_pop >= batch[0]: + # consume current batch + qty_to_pop = qty_to_pop - batch[0] stock_queue.pop(0) + if not stock_queue and qty_to_pop: + # stock finished, qty still remains to be withdrawn + # negative stock, keep in as a negative batch + stock_queue.append([-qty_to_pop, batch[1]]) + break + else: - # all from current batch - incoming_cost += flt(qty_to_pop) * flt(batch[1]) - batch[0] -= qty_to_pop + # qty found in current batch + # consume it and exit + batch[0] = batch[0] - qty_to_pop qty_to_pop = 0 stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue)) stock_qty = sum((flt(batch[0]) for batch in stock_queue)) - valuation_rate = stock_qty and (stock_value / flt(stock_qty)) or 0 + valuation_rate = (stock_value / flt(stock_qty)) if stock_qty else 0 - return valuation_rate + return abs(valuation_rate) + +def intialize_stock_queue(stock_queue, item_code, warehouse): + if not stock_queue: + from erpnext.controllers.stock_controller import get_valuation_rate + estimated_val_rate = get_valuation_rate(item_code, warehouse) + stock_queue.append([0, estimated_val_rate]) def _raise_exceptions(args, verbose=1): deficiency = min(e["diff"] for e in _exceptions) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 7264f360b9..b444e84193 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -5,7 +5,6 @@ import frappe from frappe import _ import json from frappe.utils import flt, cstr, nowdate, add_days, cint -from frappe.defaults import get_global_default from frappe.utils.email_lib import sendmail from erpnext.accounts.utils import get_fiscal_year, FiscalYearError @@ -94,7 +93,7 @@ def get_valuation_method(item_code): """get valuation method from item or default""" val_method = frappe.db.get_value('Item', item_code, 'valuation_method') if not val_method: - val_method = get_global_default('valuation_method') or "FIFO" + val_method = frappe.db.get_value("Stock Settings", None, "valuation_method") or "FIFO" return val_method def get_fifo_rate(previous_stock_queue, qty): diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py index 51b472e155..b5eec231d7 100644 --- a/erpnext/utilities/repost_stock.py +++ b/erpnext/utilities/repost_stock.py @@ -213,10 +213,13 @@ def repost_all_stock_vouchers(): vouchers = frappe.db.sql("""select distinct voucher_type, voucher_no from `tabStock Ledger Entry` order by posting_date, posting_time, name""") + print len(vouchers) rejected = [] - # vouchers = [["Purchase Receipt", "GRN00062"]] + # vouchers = [["Delivery Note", "DN00060"]] + i = 0 for voucher_type, voucher_no in vouchers: - print voucher_type, voucher_no + i+=1 + print i try: for dt in ["Stock Ledger Entry", "GL Entry"]: frappe.db.sql("""delete from `tab%s` where voucher_type=%s and voucher_no=%s"""% From 7820b171d31b3cf409bf60ce2f77703655926215 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 10 Oct 2014 18:02:23 +0530 Subject: [PATCH 2/5] Stock balance grid report deprecated and moved to server side --- erpnext/patches.txt | 3 +- .../__init__.py | 0 .../stock_balance.js} | 8 ++--- .../stock_balance.json} | 11 +++---- .../stock_balance.py} | 30 +++++++++++-------- 5 files changed, 30 insertions(+), 22 deletions(-) rename erpnext/stock/report/{warehouse_wise_stock_balance => stock_balance}/__init__.py (100%) rename erpnext/stock/report/{warehouse_wise_stock_balance/warehouse_wise_stock_balance.js => stock_balance/stock_balance.js} (75%) rename erpnext/stock/report/{warehouse_wise_stock_balance/warehouse_wise_stock_balance.json => stock_balance/stock_balance.json} (57%) rename erpnext/stock/report/{warehouse_wise_stock_balance/warehouse_wise_stock_balance.py => stock_balance/stock_balance.py} (80%) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 29049f7def..bd2d436453 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -81,4 +81,5 @@ erpnext.patches.v4_2.default_website_style erpnext.patches.v4_2.set_company_country erpnext.patches.v4_2.update_sales_order_invoice_field_name erpnext.patches.v4_2.cost_of_production_cycle -erpnext.patches.v4_2.seprate_manufacture_and_repack \ No newline at end of file +erpnext.patches.v4_2.seprate_manufacture_and_repack +execute:frappe.delete_doc("Report", "Warehouse-Wise Stock Balance") diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/__init__.py b/erpnext/stock/report/stock_balance/__init__.py similarity index 100% rename from erpnext/stock/report/warehouse_wise_stock_balance/__init__.py rename to erpnext/stock/report/stock_balance/__init__.py diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js similarity index 75% rename from erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js rename to erpnext/stock/report/stock_balance/stock_balance.js index 2543fa4667..c0aed51845 100644 --- a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js +++ b/erpnext/stock/report/stock_balance/stock_balance.js @@ -1,7 +1,7 @@ -// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt +// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors +// For license information, please see license.txt -frappe.query_reports["Warehouse-Wise Stock Balance"] = { +frappe.query_reports["Stock Balance"] = { "filters": [ { "fieldname":"from_date", @@ -18,4 +18,4 @@ frappe.query_reports["Warehouse-Wise Stock Balance"] = { "default": frappe.datetime.get_today() } ] -} \ No newline at end of file +} diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.json b/erpnext/stock/report/stock_balance/stock_balance.json similarity index 57% rename from erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.json rename to erpnext/stock/report/stock_balance/stock_balance.json index f911956111..ab331dc3c2 100644 --- a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.json +++ b/erpnext/stock/report/stock_balance/stock_balance.json @@ -1,16 +1,17 @@ { + "add_total_row": 0, "apply_user_permissions": 1, - "creation": "2013-06-05 11:00:31", + "creation": "2014-10-10 17:58:11.577901", + "disabled": 0, "docstatus": 0, "doctype": "Report", - "idx": 1, "is_standard": "Yes", - "modified": "2014-06-03 07:18:17.384923", + "modified": "2014-10-10 17:58:11.577901", "modified_by": "Administrator", "module": "Stock", - "name": "Warehouse-Wise Stock Balance", + "name": "Stock Balance", "owner": "Administrator", "ref_doctype": "Stock Ledger Entry", - "report_name": "Warehouse-Wise Stock Balance", + "report_name": "Stock Balance", "report_type": "Script Report" } \ No newline at end of file diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py similarity index 80% rename from erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py rename to erpnext/stock/report/stock_balance/stock_balance.py index dc552cb773..95de739e0f 100644 --- a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -59,9 +59,9 @@ def get_conditions(filters): def get_stock_ledger_entries(filters): conditions = get_conditions(filters) return frappe.db.sql("""select item_code, warehouse, posting_date, - actual_qty, valuation_rate, stock_uom, company + actual_qty, valuation_rate, stock_uom, company, voucher_type, qty_after_transaction from `tabStock Ledger Entry` - where docstatus < 2 %s order by item_code, warehouse""" % + where docstatus < 2 %s order by posting_date, posting_time, name""" % conditions, as_dict=1) def get_item_warehouse_map(filters): @@ -80,21 +80,27 @@ def get_item_warehouse_map(filters): qty_dict = iwb_map[d.company][d.item_code][d.warehouse] qty_dict.uom = d.stock_uom + if d.voucher_type == "Stock Reconciliation": + qty_diff = flt(d.qty_after_transaction) - qty_dict.bal_qty + value_diff = flt(d.stock_value) - qty_dict.bal_val + else: + qty_diff = flt(d.actual_qty) + value_diff = flt(d.actual_qty) * flt(d.valuation_rate) + if d.posting_date < filters["from_date"]: - qty_dict.opening_qty += flt(d.actual_qty) - qty_dict.opening_val += flt(d.actual_qty) * flt(d.valuation_rate) + qty_dict.opening_qty += qty_diff + qty_dict.opening_val += value_diff elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]: qty_dict.val_rate = d.valuation_rate - - if flt(d.actual_qty) > 0: - qty_dict.in_qty += flt(d.actual_qty) - qty_dict.in_val += flt(d.actual_qty) * flt(d.valuation_rate) + if qty_diff > 0: + qty_dict.in_qty += qty_diff + qty_dict.in_val += value_diff else: - qty_dict.out_qty += abs(flt(d.actual_qty)) - qty_dict.out_val += flt(abs(flt(d.actual_qty) * flt(d.valuation_rate))) + qty_dict.out_qty += abs(qty_diff) + qty_dict.out_val += abs(value_diff) - qty_dict.bal_qty += flt(d.actual_qty) - qty_dict.bal_val += flt(d.actual_qty) * flt(d.valuation_rate) + qty_dict.bal_qty += qty_diff + qty_dict.bal_val += value_diff return iwb_map From 7c6f990cf9688ad033c18e0620d35d601819165c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 10 Oct 2014 18:03:27 +0530 Subject: [PATCH 3/5] Minor fix for moving average --- erpnext/stock/stock_ledger.py | 26 ++++++++++++-------------- erpnext/utilities/repost_stock.py | 11 +++++------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index f1ba94efab..a2614d7f79 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -6,6 +6,7 @@ import frappe from frappe import _ from frappe.utils import cint, flt, cstr, now from erpnext.stock.utils import get_valuation_method +from erpnext.controllers.stock_controller import get_valuation_rate import json # future reposting @@ -106,8 +107,7 @@ def update_entries_after(args, verbose=1): stock_queue = [[qty_after_transaction, valuation_rate]] else: if valuation_method == "Moving Average": - if flt(sle.actual_qty) > 0: - valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate) + valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate) else: valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue) @@ -256,19 +256,18 @@ def get_moving_average_values(qty_after_transaction, sle, valuation_rate): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) - if not incoming_rate: - # If wrong incoming rate - incoming_rate = valuation_rate + if flt(sle.actual_qty) > 0: + if qty_after_transaction < 0 and not valuation_rate: + # if negative stock, take current valuation rate as incoming rate + valuation_rate = incoming_rate - elif qty_after_transaction < 0 and not valuation_rate: - # if negative stock, take current valuation rate as incoming rate - valuation_rate = incoming_rate + new_stock_qty = abs(qty_after_transaction) + actual_qty + new_stock_value = (abs(qty_after_transaction) * valuation_rate) + (actual_qty * incoming_rate) - new_stock_qty = abs(qty_after_transaction) + actual_qty - new_stock_value = (abs(qty_after_transaction) * valuation_rate) + (actual_qty * incoming_rate) - - if new_stock_qty: - valuation_rate = new_stock_value / flt(new_stock_qty) + if new_stock_qty: + valuation_rate = new_stock_value / flt(new_stock_qty) + elif not valuation_rate: + valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse) return abs(valuation_rate) @@ -321,7 +320,6 @@ def get_fifo_values(qty_after_transaction, sle, stock_queue): def intialize_stock_queue(stock_queue, item_code, warehouse): if not stock_queue: - from erpnext.controllers.stock_controller import get_valuation_rate estimated_val_rate = get_valuation_rate(item_code, warehouse) stock_queue.append([0, estimated_val_rate]) diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py index b5eec231d7..7d9423db14 100644 --- a/erpnext/utilities/repost_stock.py +++ b/erpnext/utilities/repost_stock.py @@ -211,15 +211,14 @@ def reset_serial_no_status_and_warehouse(serial_nos=None): def repost_all_stock_vouchers(): vouchers = frappe.db.sql("""select distinct voucher_type, voucher_no - from `tabStock Ledger Entry` order by posting_date, posting_time, name""") + from `tabStock Ledger Entry` + order by posting_date, posting_time, name""") - print len(vouchers) rejected = [] - # vouchers = [["Delivery Note", "DN00060"]] i = 0 for voucher_type, voucher_no in vouchers: i+=1 - print i + print i, "/", len(vouchers) try: for dt in ["Stock Ledger Entry", "GL Entry"]: frappe.db.sql("""delete from `tab%s` where voucher_type=%s and voucher_no=%s"""% @@ -228,8 +227,8 @@ def repost_all_stock_vouchers(): doc = frappe.get_doc(voucher_type, voucher_no) if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]: doc.get_stock_and_rate(force=1) - # elif voucher_type=="Purchase Receipt": - # doc.create_raw_materials_supplied("pr_raw_material_details") + elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes": + doc.validate() doc.update_stock_ledger() doc.make_gl_entries(repost_future_gle=False, allow_negative_stock=True) From 4f0e5db2162e3559df13a8feb94b677cbe6b1edc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 10 Oct 2014 20:54:57 +0530 Subject: [PATCH 4/5] Stock balance grid report deprecated and moved to server side --- erpnext/config/stock.py | 16 +- erpnext/stock/page/stock_balance/README.md | 1 - erpnext/stock/page/stock_balance/__init__.py | 0 .../stock/page/stock_balance/stock_balance.js | 192 ------------------ .../page/stock_balance/stock_balance.json | 23 --- 5 files changed, 5 insertions(+), 227 deletions(-) delete mode 100644 erpnext/stock/page/stock_balance/README.md delete mode 100644 erpnext/stock/page/stock_balance/__init__.py delete mode 100644 erpnext/stock/page/stock_balance/stock_balance.js delete mode 100644 erpnext/stock/page/stock_balance/stock_balance.json diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index 7a4345e4d2..04e45d45a1 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -142,10 +142,10 @@ def get_data(): "doctype": "Item", }, { - "type": "page", - "name": "stock-balance", - "label": _("Stock Balance"), - "icon": "icon-table", + "type": "report", + "is_query_report": True, + "name": "Stock Balance", + "doctype": "Warehouse" }, { "type": "report", @@ -170,13 +170,7 @@ def get_data(): "name": "stock-analytics", "label": _("Stock Analytics"), "icon": "icon-bar-chart" - }, - { - "type": "report", - "is_query_report": True, - "name": "Warehouse-Wise Stock Balance", - "doctype": "Warehouse" - }, + } ] }, { diff --git a/erpnext/stock/page/stock_balance/README.md b/erpnext/stock/page/stock_balance/README.md deleted file mode 100644 index 6522aeb099..0000000000 --- a/erpnext/stock/page/stock_balance/README.md +++ /dev/null @@ -1 +0,0 @@ -Stock balances on a particular day, per warehouse. \ No newline at end of file diff --git a/erpnext/stock/page/stock_balance/__init__.py b/erpnext/stock/page/stock_balance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js deleted file mode 100644 index 198d317824..0000000000 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.require("assets/erpnext/js/stock_analytics.js"); - -frappe.pages['stock-balance'].onload = function(wrapper) { - frappe.ui.make_app_page({ - parent: wrapper, - title: __('Stock Balance'), - single_column: true - }); - - new erpnext.StockBalance(wrapper); - - wrapper.appframe.add_module_icon("Stock"); -} - -erpnext.StockBalance = erpnext.StockAnalytics.extend({ - init: function(wrapper) { - this._super(wrapper, { - title: __("Stock Balance"), - doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand", - "Stock Entry", "Project", "Serial No"], - }); - }, - setup_columns: function() { - this.columns = [ - {id: "name", name: __("Item"), field: "name", width: 300, - formatter: this.tree_formatter}, - {id: "item_name", name: __("Item Name"), field: "item_name", width: 100}, - {id: "description", name: __("Description"), field: "description", width: 200, - formatter: this.text_formatter}, - {id: "brand", name: __("Brand"), field: "brand", width: 100}, - {id: "stock_uom", name: __("UOM"), field: "stock_uom", width: 100}, - {id: "opening_qty", name: __("Opening Qty"), field: "opening_qty", width: 100, - formatter: this.currency_formatter}, - {id: "inflow_qty", name: __("In Qty"), field: "inflow_qty", width: 100, - formatter: this.currency_formatter}, - {id: "outflow_qty", name: __("Out Qty"), field: "outflow_qty", width: 100, - formatter: this.currency_formatter}, - {id: "closing_qty", name: __("Closing Qty"), field: "closing_qty", width: 100, - formatter: this.currency_formatter}, - - {id: "opening_value", name: __("Opening Value"), field: "opening_value", width: 100, - formatter: this.currency_formatter}, - {id: "inflow_value", name: __("In Value"), field: "inflow_value", width: 100, - formatter: this.currency_formatter}, - {id: "outflow_value", name: __("Out Value"), field: "outflow_value", width: 100, - formatter: this.currency_formatter}, - {id: "closing_value", name: __("Closing Value"), field: "closing_value", width: 100, - formatter: this.currency_formatter}, - {id: "valuation_rate", name: __("Valuation Rate"), field: "valuation_rate", width: 100, - formatter: this.currency_formatter}, - ]; - }, - - filters: [ - {fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand", - default_value: __("Select Brand..."), filter: function(val, item, opts) { - return val == opts.default_value || item.brand == val || item._show; - }, link_formatter: {filter_input: "brand"}}, - {fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse", - default_value: __("Select Warehouse..."), filter: function(val, item, opts, me) { - return me.apply_zero_filter(val, item, opts, me); - }}, - {fieldtype:"Select", label: __("Project"), link:"Project", fieldname: "project", - default_value: __("Select Project..."), filter: function(val, item, opts, me) { - return me.apply_zero_filter(val, item, opts, me); - }, link_formatter: {filter_input: "project"}}, - {fieldtype:"Date", label: __("From Date"), fieldname: "from_date"}, - {fieldtype:"Label", label: __("To")}, - {fieldtype:"Date", label: __("To Date"), fieldname: "to_date"}, - {fieldtype:"Button", label: __("Refresh"), icon:"icon-refresh icon-white"}, - {fieldtype:"Button", label: __("Reset Filters"), icon: "icon-filter"} - ], - - setup_plot_check: function() { - return; - }, - - prepare_data: function() { - this.stock_entry_map = this.make_name_map(frappe.report_dump.data["Stock Entry"], "name"); - this._super(); - }, - - prepare_balances: function() { - var me = this; - var from_date = dateutil.str_to_obj(this.from_date); - var to_date = dateutil.str_to_obj(this.to_date); - var data = frappe.report_dump.data["Stock Ledger Entry"]; - - this.item_warehouse = {}; - this.serialized_buying_rates = this.get_serialized_buying_rates(); - - for(var i=0, j=data.length; i 0) - item.valuation_rate = flt(item.closing_value) / flt(item.closing_qty); - else item.valuation_rate = 0.0 - }); - }, - - update_groups: function() { - var me = this; - - $.each(this.data, function(i, item) { - // update groups - if(!item.is_group && me.apply_filter(item, "brand")) { - var parent = me.parent_map[item.name]; - while(parent) { - parent_group = me.item_by_name[parent]; - $.each(me.columns, function(c, col) { - if (col.formatter == me.currency_formatter && col.field != "valuation_rate") { - parent_group[col.field] = flt(parent_group[col.field]) + flt(item[col.field]); - } - }); - - // show parent if filtered by brand - if(item.brand == me.brand) - parent_group._show = true; - - parent = me.parent_map[parent]; - } - } - }); - }, - - get_plot_data: function() { - return; - } -}); diff --git a/erpnext/stock/page/stock_balance/stock_balance.json b/erpnext/stock/page/stock_balance/stock_balance.json deleted file mode 100644 index 6f25be4407..0000000000 --- a/erpnext/stock/page/stock_balance/stock_balance.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "creation": "2012-12-27 18:57:47.000000", - "docstatus": 0, - "doctype": "Page", - "icon": "icon-table", - "idx": 1, - "modified": "2013-07-11 14:44:15.000000", - "modified_by": "Administrator", - "module": "Stock", - "name": "stock-balance", - "owner": "Administrator", - "page_name": "stock-balance", - "roles": [ - { - "role": "Material Manager" - }, - { - "role": "Analytics" - } - ], - "standard": "Yes", - "title": "Stock Balance" -} \ No newline at end of file From 70ec88b733880958bae15f951b54179053d86340 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 10 Oct 2014 21:22:46 +0530 Subject: [PATCH 5/5] fixed test cases --- erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py | 2 +- erpnext/stock/doctype/stock_entry/test_stock_entry.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 51c85ce48b..67f621aa01 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -95,7 +95,7 @@ class TestPurchaseReceipt(unittest.TestCase): pr.insert() self.assertEquals(len(pr.get("pr_raw_material_details")), 2) - self.assertEquals(pr.get("purchase_receipt_details")[0].rm_supp_cost, 70000.0) + self.assertEquals(pr.get("purchase_receipt_details")[0].rm_supp_cost, 20750.0) def test_serial_no_supplier(self): diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 7654cbcccd..e010bd19b9 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -939,3 +939,5 @@ def make_stock_entry(item, source, target, qty, incoming_rate=None): s.insert() s.submit() return s + +test_records = frappe.get_test_records('Stock Entry')