diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 31f7113c37..a2bf78c449 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -7,7 +7,7 @@ import frappe.defaults from frappe.utils import cint, cstr, flt from frappe import _, msgprint, throw from erpnext.accounts.party import get_party_account, get_due_date -from erpnext.controllers.stock_controller import update_gl_entries_after, block_negative_stock +from erpnext.controllers.stock_controller import update_gl_entries_after from frappe.model.mapper import get_mapped_doc from erpnext.controllers.selling_controller import SellingController @@ -456,8 +456,8 @@ class SalesInvoice(SellingController): self.make_sl_entries(sl_entries) - def make_gl_entries(self, repost_future_gle=True, allow_negative_stock=False): - gl_entries = self.get_gl_entries(allow_negative_stock=allow_negative_stock) + def make_gl_entries(self, repost_future_gle=True): + gl_entries = self.get_gl_entries() if gl_entries: from erpnext.accounts.general_ledger import make_gl_entries @@ -476,7 +476,7 @@ class SalesInvoice(SellingController): items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items) - def get_gl_entries(self, warehouse_account=None, allow_negative_stock=False): + def get_gl_entries(self, warehouse_account=None): from erpnext.accounts.general_ledger import merge_similar_entries gl_entries = [] @@ -485,7 +485,7 @@ class SalesInvoice(SellingController): self.make_tax_gl_entries(gl_entries) - self.make_item_gl_entries(gl_entries, allow_negative_stock) + self.make_item_gl_entries(gl_entries) # merge gl entries before adding pos entries gl_entries = merge_similar_entries(gl_entries) @@ -520,7 +520,7 @@ class SalesInvoice(SellingController): }) ) - def make_item_gl_entries(self, gl_entries, allow_negative_stock=False): + def make_item_gl_entries(self, gl_entries): # income account gl entries for item in self.get("entries"): if flt(item.base_amount): @@ -537,7 +537,7 @@ class SalesInvoice(SellingController): # expense account gl entries if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \ and cint(self.update_stock): - gl_entries += super(SalesInvoice, self).get_gl_entries(allow_negative_stock=allow_negative_stock) + gl_entries += super(SalesInvoice, self).get_gl_entries() def make_pos_gl_entries(self, gl_entries): if cint(self.is_pos) and self.cash_bank_account and self.paid_amount: diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index d7c7a43568..0f8eca4a7c 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -11,7 +11,7 @@ from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map class StockController(AccountsController): - def make_gl_entries(self, repost_future_gle=True, allow_negative_stock=False): + def make_gl_entries(self, repost_future_gle=True): if self.docstatus == 2: delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) @@ -19,18 +19,16 @@ class StockController(AccountsController): warehouse_account = get_warehouse_account() if self.docstatus==1: - gl_entries = self.get_gl_entries(warehouse_account, allow_negative_stock=allow_negative_stock) + gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries) if repost_future_gle: items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, - warehouse_account, allow_negative_stock) + warehouse_account) def get_gl_entries(self, warehouse_account=None, default_expense_account=None, - default_cost_center=None, allow_negative_stock=False): - - # block_negative_stock(allow_negative_stock) + default_cost_center=None): if not warehouse_account: warehouse_account = get_warehouse_account() @@ -219,7 +217,7 @@ class StockController(AccountsController): return serialized_items def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, - warehouse_account=None, allow_negative_stock=False): + warehouse_account=None): def _delete_gl_entries(voucher_type, voucher_no): frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) @@ -233,12 +231,12 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for for voucher_type, voucher_no in future_stock_vouchers: existing_gle = gle.get((voucher_type, voucher_no), []) voucher_obj = frappe.get_doc(voucher_type, voucher_no) - expected_gle = voucher_obj.get_gl_entries(warehouse_account, allow_negative_stock=allow_negative_stock) + expected_gle = voucher_obj.get_gl_entries(warehouse_account) if expected_gle: if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle): _delete_gl_entries(voucher_type, voucher_no) - voucher_obj.make_gl_entries(repost_future_gle=False, allow_negative_stock=allow_negative_stock) + voucher_obj.make_gl_entries(repost_future_gle=False) else: _delete_gl_entries(voucher_type, voucher_no) @@ -290,8 +288,3 @@ def get_warehouse_account(): warehouse_account = dict(frappe.db.sql("""select master_name, name from tabAccount where account_type = 'Warehouse' and ifnull(master_name, '') != ''""")) return warehouse_account - -def block_negative_stock(allow_negative_stock=False): - if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) and not allow_negative_stock: - if cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")): - frappe.throw(_("Negative stock is not allowed in case of Perpetual Inventory, please disable it from Stock Settings")) diff --git a/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py b/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py index 3c554ae0f0..a7da8643ec 100644 --- a/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py +++ b/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py @@ -7,7 +7,7 @@ from frappe.utils import flt def execute(): from erpnext.utilities.repost_stock import repost - repost() + repost(allow_zero_rate=True) warehouse_account = frappe.db.sql("""select name, master_name from tabAccount where ifnull(account_type, '') = 'Warehouse'""") @@ -40,7 +40,7 @@ def execute(): where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) voucher = frappe.get_doc(voucher_type, voucher_no) - voucher.make_gl_entries(repost_future_gle=False, allow_negative_stock=True) + voucher.make_gl_entries(repost_future_gle=False) frappe.db.commit() except Exception, e: print frappe.get_traceback() diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index ed4257fd9b..13495a0377 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -283,11 +283,8 @@ class PurchaseReceipt(BuyingController): def get_rate(self,arg): return frappe.get_doc('Purchase Common').get_rate(arg,self) - def get_gl_entries(self, warehouse_account=None, allow_negative_stock=False): + def get_gl_entries(self, warehouse_account=None): from erpnext.accounts.general_ledger import process_gl_map - from erpnext.controllers.stock_controller import block_negative_stock - - # block_negative_stock(allow_negative_stock) stock_rbnb = self.get_company_default("stock_received_but_not_billed") expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index b67090d137..530ab9af80 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -198,12 +198,12 @@ class StockReconciliation(StockController): "posting_time": self.posting_time }) - def get_gl_entries(self, warehouse_account=None, allow_negative_stock=False): + def get_gl_entries(self, warehouse_account=None): if not self.cost_center: msgprint(_("Please enter Cost Center"), raise_exception=1) return super(StockReconciliation, self).get_gl_entries(warehouse_account, - self.expense_account, self.cost_center, allow_negative_stock=allow_negative_stock) + self.expense_account, self.cost_center) def validate_expense_account(self): if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 0fbf6a8652..edbdb1aa06 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -58,7 +58,7 @@ def delete_cancelled_entry(voucher_type, voucher_no): frappe.db.sql("""delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) -def update_entries_after(args, verbose=1): +def update_entries_after(args, allow_zero_rate=False, verbose=1): """ update valution rate and qty after transaction from the current time-bucket onwards @@ -106,9 +106,9 @@ 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) + valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate, allow_zero_rate) else: - valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue) + valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue, allow_zero_rate) qty_after_transaction += flt(sle.actual_qty) @@ -251,7 +251,7 @@ def get_serialized_values(qty_after_transaction, sle, valuation_rate): return valuation_rate -def get_moving_average_values(qty_after_transaction, sle, valuation_rate): +def get_moving_average_values(qty_after_transaction, sle, valuation_rate, allow_zero_rate): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) @@ -266,11 +266,11 @@ def get_moving_average_values(qty_after_transaction, sle, valuation_rate): if new_stock_qty: valuation_rate = new_stock_value / flt(new_stock_qty) elif not valuation_rate and qty_after_transaction <= 0: - valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse) + valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, allow_zero_rate) return abs(flt(valuation_rate)) -def get_fifo_values(qty_after_transaction, sle, stock_queue): +def get_fifo_values(qty_after_transaction, sle, stock_queue, allow_zero_rate): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) @@ -290,7 +290,7 @@ def get_fifo_values(qty_after_transaction, sle, stock_queue): qty_to_pop = abs(actual_qty) while qty_to_pop: if not stock_queue: - stock_queue.append([0, get_valuation_rate(sle.item_code, sle.warehouse) + stock_queue.append([0, get_valuation_rate(sle.item_code, sle.warehouse, allow_zero_rate) if qty_after_transaction <= 0 else 0]) batch = stock_queue[0] @@ -349,7 +349,7 @@ def get_previous_sle(args, for_update=False): "desc", "limit 1", for_update=for_update) return sle and sle[0] or {} -def get_valuation_rate(item_code, warehouse): +def get_valuation_rate(item_code, warehouse, allow_zero_rate=False): last_valuation_rate = frappe.db.sql("""select valuation_rate from `tabStock Ledger Entry` where item_code = %s and warehouse = %s @@ -367,7 +367,7 @@ def get_valuation_rate(item_code, warehouse): if not valuation_rate: valuation_rate = frappe.db.get_value("Item Price", {"item_code": item_code, "buying": 1}, "price_list_rate") - if not valuation_rate and cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")): + if not allow_zero_rate and not valuation_rate and cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")): frappe.throw(_("Purchase rate for item: {0} not found, which is required to book accounting entry (expense). Please mention item price against a buying price list.").format(item_code)) return valuation_rate diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py index 89494ad2ca..4be95911c8 100644 --- a/erpnext/utilities/repost_stock.py +++ b/erpnext/utilities/repost_stock.py @@ -9,7 +9,7 @@ from erpnext.stock.utils import update_bin from erpnext.stock.stock_ledger import update_entries_after from erpnext.accounts.utils import get_fiscal_year -def repost(allow_negative_stock=False): +def repost(allow_negative_stock=False, allow_zero_rate=False): """ Repost everything! """ @@ -23,7 +23,7 @@ def repost(allow_negative_stock=False): union select item_code, warehouse from `tabStock Ledger Entry`) a"""): try: - repost_stock(d[0], d[1]) + repost_stock(d[0], d[1], allow_zero_rate) frappe.db.commit() except: frappe.db.rollback() @@ -33,8 +33,8 @@ def repost(allow_negative_stock=False): frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) frappe.db.auto_commit_on_many_writes = 0 -def repost_stock(item_code, warehouse): - repost_actual_qty(item_code, warehouse) +def repost_stock(item_code, warehouse, allow_zero_rate=False): + repost_actual_qty(item_code, warehouse, allow_zero_rate) if item_code and warehouse: update_bin_qty(item_code, warehouse, { @@ -44,9 +44,9 @@ def repost_stock(item_code, warehouse): "planned_qty": get_planned_qty(item_code, warehouse) }) -def repost_actual_qty(item_code, warehouse): +def repost_actual_qty(item_code, warehouse, allow_zero_rate=False): try: - update_entries_after({ "item_code": item_code, "warehouse": warehouse }) + update_entries_after({ "item_code": item_code, "warehouse": warehouse }, allow_zero_rate) except: pass