From 7a75e10a61b9b0b9e9563df454d662c893029152 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 17 Sep 2013 10:21:20 +0530 Subject: [PATCH] [fix] [minor] perpetual inventory: account for each warehouse --- accounts/doctype/account/account.js | 31 +++++---- accounts/doctype/account/account.py | 54 ++++++++++----- accounts/doctype/account/account.txt | 4 +- .../accounts_settings/accounts_settings.py | 8 ++- .../accounts_settings/accounts_settings.txt | 4 +- .../doctype/sales_invoice/sales_invoice.py | 2 - .../sales_invoice/test_sales_invoice.py | 38 ++++++----- controllers/accounts_controller.py | 61 ++++++++--------- controllers/selling_controller.py | 2 +- controllers/stock_controller.py | 18 +++-- selling/doctype/customer/customer.py | 4 +- setup/doctype/company/company.py | 15 ++--- .../delivery_note/test_delivery_note.py | 22 +++---- .../purchase_receipt/test_purchase_receipt.py | 24 ++++--- stock/doctype/warehouse/test_warehouse.py | 6 +- stock/doctype/warehouse/warehouse.js | 4 +- stock/doctype/warehouse/warehouse.py | 66 +++++++++++++++---- stock/doctype/warehouse/warehouse.txt | 49 +++++--------- 18 files changed, 241 insertions(+), 171 deletions(-) diff --git a/accounts/doctype/account/account.js b/accounts/doctype/account/account.js index 6b06572d8e..99d8d588d1 100644 --- a/accounts/doctype/account/account.js +++ b/accounts/doctype/account/account.js @@ -32,10 +32,10 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { } else { // credit days and type if customer or supplier cur_frm.set_intro(null); - cur_frm.toggle_display(['credit_days', 'credit_limit', 'master_name'], - in_list(['Customer', 'Supplier'], doc.master_type)); - - // hide tax_rate + cur_frm.toggle_display(['credit_days', 'credit_limit'], in_list(['Customer', 'Supplier'], + doc.master_type)); + + cur_frm.cscript.master_type(doc, cdt, cdn); cur_frm.cscript.account_type(doc, cdt, cdn); // show / hide convert buttons @@ -44,7 +44,10 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { } cur_frm.cscript.master_type = function(doc, cdt, cdn) { - cur_frm.toggle_display(['credit_days', 'credit_limit', 'master_name'], + cur_frm.toggle_display(['credit_days', 'credit_limit'], in_list(['Customer', 'Supplier'], + doc.master_type)); + + cur_frm.toggle_display('master_name', doc.account_type=='Warehouse' || in_list(['Customer', 'Supplier'], doc.master_type)); } @@ -58,10 +61,10 @@ cur_frm.add_fetch('parent_account', 'is_pl_account', 'is_pl_account'); // ----------------------------------------- cur_frm.cscript.account_type = function(doc, cdt, cdn) { if(doc.group_or_ledger=='Ledger') { - cur_frm.toggle_display(['tax_rate'], - doc.account_type == 'Tax'); - cur_frm.toggle_display(['master_type', 'master_name'], - cstr(doc.account_type)==''); + cur_frm.toggle_display(['tax_rate'], doc.account_type == 'Tax'); + cur_frm.toggle_display('master_type', cstr(doc.account_type)==''); + cur_frm.toggle_display('master_name', doc.account_type=='Warehouse' || + in_list(['Customer', 'Supplier'], doc.master_type)); } } @@ -109,11 +112,15 @@ cur_frm.cscript.convert_to_group = function(doc, cdt, cdn) { } cur_frm.fields_dict['master_name'].get_query = function(doc) { - if (doc.master_type) { + if (doc.master_type || doc.account_type=="Warehouse") { + var dt = doc.master_type || "Warehouse"; return { - doctype: doc.master_type, + doctype: dt, query: "accounts.doctype.account.account.get_master_name", - filters: { "master_type": doc.master_type } + filters: { + "master_type": dt, + "company": doc.company + } } } } diff --git a/accounts/doctype/account/account.py b/accounts/doctype/account/account.py index d3d467ffb4..3d305d3638 100644 --- a/accounts/doctype/account/account.py +++ b/accounts/doctype/account/account.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import flt, fmt_money +from webnotes.utils import flt, fmt_money, cstr, cint from webnotes import msgprint, _ sql = webnotes.conn.sql @@ -16,13 +16,25 @@ class DocType: self.nsm_parent_field = 'parent_account' def autoname(self): - """Append abbreviation to company on naming""" self.doc.name = self.doc.account_name.strip() + ' - ' + \ webnotes.conn.get_value("Company", self.doc.company, "abbr") def get_address(self): - address = webnotes.conn.get_value(self.doc.master_type, self.doc.master_name, "address") - return {'address': address} + return { + 'address': webnotes.conn.get_value(self.doc.master_type, + self.doc.master_name, "address") + } + + def validate(self): + self.validate_master_name() + self.validate_parent() + self.validate_duplicate_account() + self.validate_root_details() + self.validate_mandatory() + self.validate_warehouse_account() + + if not self.doc.parent_account: + self.doc.parent_account = '' def validate_master_name(self): """Remind to add master name""" @@ -109,16 +121,26 @@ class DocType: msgprint("Debit or Credit field is mandatory", raise_exception=1) if not self.doc.is_pl_account: msgprint("Is PL Account field is mandatory", raise_exception=1) + + def validate_warehouse_account(self): + if not cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")): + return + + if self.doc.account_type == "Warehouse": + old_warehouse = cstr(webnotes.conn.get_value("Account", self.doc.name, "master_name")) + if old_warehouse != cstr(self.doc.master_name): + if old_warehouse: + self.validate_warehouse(old_warehouse) + if self.doc.master_name: + self.validate_warehouse(self.doc.master_name) + else: + webnotes.throw(_("Master Name is mandatory if account type is Warehouse")) + + def validate_warehouse(self, warehouse): + if webnotes.conn.get_value("Stock Ledger Entry", {"warehouse": warehouse}): + webnotes.throw(_("Stock transactions exist against warehouse ") + warehouse + + _(" .You can not assign / modify / remove Master Name")) - def validate(self): - self.validate_master_name() - self.validate_parent() - self.validate_duplicate_account() - self.validate_root_details() - self.validate_mandatory() - - if not self.doc.parent_account: - self.doc.parent_account = '' def update_nsm_model(self): """update lft, rgt indices for nested set model""" @@ -198,9 +220,11 @@ class DocType: return " - ".join(parts) def get_master_name(doctype, txt, searchfield, start, page_len, filters): - return webnotes.conn.sql("""select name from `tab%s` where %s like %s + conditions = (" and company='%s'"% filters["company"]) if doctype == "Warehouse" else "" + + return webnotes.conn.sql("""select name from `tab%s` where %s like %s %s order by name limit %s, %s""" % - (filters["master_type"], searchfield, "%s", "%s", "%s"), + (filters["master_type"], searchfield, "%s", conditions, "%s", "%s"), ("%%%s%%" % txt, start, page_len), as_list=1) def get_parent_account(doctype, txt, searchfield, start, page_len, filters): diff --git a/accounts/doctype/account/account.txt b/accounts/doctype/account/account.txt index 7a6ebf8dac..87006f319c 100644 --- a/accounts/doctype/account/account.txt +++ b/accounts/doctype/account/account.txt @@ -2,7 +2,7 @@ { "creation": "2013-01-30 12:49:46", "docstatus": 0, - "modified": "2013-07-05 14:23:30", + "modified": "2013-09-16 12:21:10", "modified_by": "Administrator", "owner": "Administrator" }, @@ -162,7 +162,7 @@ "label": "Account Type", "oldfieldname": "account_type", "oldfieldtype": "Select", - "options": "\nFixed Asset Account\nBank or Cash\nExpense Account\nTax\nIncome Account\nChargeable", + "options": "\nFixed Asset Account\nBank or Cash\nExpense Account\nTax\nIncome Account\nChargeable\nWarehouse", "permlevel": 0, "search_index": 0 }, diff --git a/accounts/doctype/accounts_settings/accounts_settings.py b/accounts/doctype/accounts_settings/accounts_settings.py index b18f14d994..0d106e8e8b 100644 --- a/accounts/doctype/accounts_settings/accounts_settings.py +++ b/accounts/doctype/accounts_settings/accounts_settings.py @@ -13,5 +13,9 @@ class DocType: self.doc, self.doclist = d, dl def on_update(self): - for key in ["auto_accounting_for_stock"]: - webnotes.conn.set_default(key, self.doc.fields.get(key, '')) + webnotes.conn.set_default("auto_accounting_for_stock", self.doc.auto_accounting_for_stock) + + if self.doc.auto_accounting_for_stock: + for wh in webnotes.conn.sql("select name from `tabWarehouse`"): + wh_bean = webnotes.bean("Warehouse", wh[0]) + wh_bean.save() \ No newline at end of file diff --git a/accounts/doctype/accounts_settings/accounts_settings.txt b/accounts/doctype/accounts_settings/accounts_settings.txt index 9dbed26db5..868b660677 100644 --- a/accounts/doctype/accounts_settings/accounts_settings.txt +++ b/accounts/doctype/accounts_settings/accounts_settings.txt @@ -2,7 +2,7 @@ { "creation": "2013-06-24 15:49:57", "docstatus": 0, - "modified": "2013-08-28 18:55:43", + "modified": "2013-09-16 14:24:39", "modified_by": "Administrator", "owner": "Administrator" }, @@ -44,7 +44,7 @@ "doctype": "DocField", "fieldname": "auto_accounting_for_stock", "fieldtype": "Check", - "label": "Make Accounting Entry For Every Stock Entry" + "label": "Make Accounting Entry For Every Stock Movement" }, { "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.", diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index fe2ed63f24..c8eea1b4d5 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -192,8 +192,6 @@ class DocType(SellingController): pos = get_pos_settings(self.doc.company) if pos: - self.doc.conversion_rate = flt(pos.conversion_rate) - if not for_validate: self.doc.customer = pos.customer self.set_customer_defaults() diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index c2b9c8f071..736cf42ae7 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -6,10 +6,12 @@ import unittest, json from webnotes.utils import flt, cint from webnotes.model.bean import DocstatusTransitionError, TimestampMismatchError from accounts.utils import get_stock_and_account_difference +from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory class TestSalesInvoice(unittest.TestCase): def make(self): w = webnotes.bean(copy=test_records[0]) + w.doc.is_pos = 0 w.insert() w.submit() return w @@ -93,7 +95,6 @@ class TestSalesInvoice(unittest.TestCase): si.doclist[1].ref_rate = 1 si.doclist[2].export_rate = 3 si.doclist[2].ref_rate = 3 - si.run_method("calculate_taxes_and_totals") si.insert() expected_values = { @@ -259,6 +260,7 @@ class TestSalesInvoice(unittest.TestCase): def test_payment(self): w = self.make() + from accounts.doctype.journal_voucher.test_journal_voucher \ import test_records as jv_test_records @@ -298,8 +300,8 @@ class TestSalesInvoice(unittest.TestCase): "Batched for Billing") def test_sales_invoice_gl_entry_without_aii(self): - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) self.clear_stock_account_balance() + set_perpetual_inventory(0) si = webnotes.bean(copy=test_records[1]) si.insert() si.submit() @@ -331,10 +333,8 @@ class TestSalesInvoice(unittest.TestCase): self.assertFalse(gle) def test_pos_gl_entry_with_aii(self): - webnotes.conn.sql("delete from `tabStock Ledger Entry`") - webnotes.conn.sql("delete from `tabGL Entry`") - webnotes.conn.sql("delete from `tabBin`") - webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) + self.clear_stock_account_balance() + set_perpetual_inventory() self._insert_purchase_receipt() self._insert_pos_settings() @@ -359,18 +359,19 @@ class TestSalesInvoice(unittest.TestCase): ["_Test Item", "_Test Warehouse - _TC", -1.0]) # check gl entries - gl_entries = webnotes.conn.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s order by account asc, debit asc""", si.doc.name, as_dict=1) self.assertTrue(gl_entries) + + stock_in_hand = webnotes.conn.get_value("Account", {"master_name": "_Test Warehouse - _TC"}) expected_gl_entries = sorted([ [si.doc.debit_to, 630.0, 0.0], [pos[1]["income_account"], 0.0, 500.0], [pos[2]["account_head"], 0.0, 80.0], [pos[3]["account_head"], 0.0, 50.0], - ["_Test Account Stock In Hand - _TC", 0.0, 75.0], + [stock_in_hand, 0.0, 75.0], [pos[1]["expense_account"], 75.0, 0.0], [si.doc.debit_to, 0.0, 600.0], ["_Test Account Bank Account - _TC", 600.0, 0.0] @@ -389,12 +390,13 @@ class TestSalesInvoice(unittest.TestCase): self.assertFalse(gle) - self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"])) + self.assertFalse(get_stock_and_account_difference([stock_in_hand])) - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) + set_perpetual_inventory(0) - def test_sales_invoice_gl_entry_with_aii_no_item_code(self): - webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) + def test_sales_invoice_gl_entry_with_aii_no_item_code(self): + self.clear_stock_account_balance() + set_perpetual_inventory() si_copy = webnotes.copy_doclist(test_records[1]) si_copy[1]["item_code"] = None @@ -417,12 +419,12 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(expected_values[i][0], gle.account) self.assertEquals(expected_values[i][1], gle.debit) self.assertEquals(expected_values[i][2], gle.credit) - - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) - - def test_sales_invoice_gl_entry_with_aii_non_stock_item(self): - webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) + set_perpetual_inventory(0) + + def test_sales_invoice_gl_entry_with_aii_non_stock_item(self): + self.clear_stock_account_balance() + set_perpetual_inventory() si_copy = webnotes.copy_doclist(test_records[1]) si_copy[1]["item_code"] = "_Test Non Stock Item" si = webnotes.bean(si_copy) @@ -445,7 +447,7 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(expected_values[i][1], gle.debit) self.assertEquals(expected_values[i][2], gle.credit) - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) + set_perpetual_inventory(0) def _insert_purchase_receipt(self): from stock.doctype.purchase_receipt.test_purchase_receipt import test_records \ diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index 6761092a99..7c3855f628 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import webnotes from webnotes import _, msgprint from webnotes.utils import flt, cint, today, cstr -from setup.utils import get_company_currency, get_price_list_currency +from setup.utils import get_company_currency from accounts.utils import get_fiscal_year, validate_fiscal_year from utilities.transaction_base import TransactionBase, validate_conversion_rate import json @@ -13,7 +13,6 @@ import json class AccountsController(TransactionBase): def validate(self): self.set_missing_values(for_validate=True) - self.validate_date_with_fiscal_year() if self.meta.get_field("currency"): self.calculate_taxes_and_totals() @@ -54,35 +53,37 @@ class AccountsController(TransactionBase): self.doc.doctype + _(" can not be made."), raise_exception=1) def set_price_list_currency(self, buying_or_selling): - company_currency = get_company_currency(self.doc.company) - fieldname = buying_or_selling.lower() + "_price_list" - # TODO - change this, since price list now has only one currency allowed - if self.meta.get_field(fieldname) and self.doc.fields.get(fieldname) and \ - not self.doc.price_list_currency: - self.doc.fields.update(get_price_list_currency(self.doc.fields.get(fieldname))) - - if self.doc.price_list_currency: - if not self.doc.plc_conversion_rate: - if self.doc.price_list_currency == company_currency: - self.doc.plc_conversion_rate = 1.0 - else: - exchange = self.doc.price_list_currency + "-" + company_currency - self.doc.plc_conversion_rate = flt(webnotes.conn.get_value("Currency Exchange", - exchange, "exchange_rate")) - - if not self.doc.currency: - self.doc.currency = self.doc.price_list_currency - self.doc.conversion_rate = self.doc.plc_conversion_rate - if self.meta.get_field("currency"): - if self.doc.currency and self.doc.currency != company_currency: - if not self.doc.conversion_rate: - exchange = self.doc.currency + "-" + company_currency - self.doc.conversion_rate = flt(webnotes.conn.get_value("Currency Exchange", - exchange, "exchange_rate")) - else: - self.doc.conversion_rate = 1 - + company_currency = get_company_currency(self.doc.company) + + # price list part + fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \ + else "buying_price_list" + if self.meta.get_field(fieldname) and self.doc.fields.get(fieldname): + if not self.doc.price_list_currency: + self.doc.price_list_currency = webnotes.conn.get_value("Price List", + self.doc.fields.get(fieldame), "currency") + + if self.doc.price_list_currency == company_currency: + self.doc.plc_conversion_rate = 1.0 + elif not self.doc.plc_conversion_rate: + self.doc.plc_conversion_rate = self.get_exchange_rate( + self.doc.price_list_currency, company_currency) + + # currency + if not self.doc.currency: + self.doc.currency = self.doc.price_list_currency + self.doc.conversion_rate = self.doc.plc_conversion_rate + elif self.doc.currency == company_currency: + self.doc.conversion_rate = 1.0 + elif not self.doc.conversion_rate: + self.doc.conversion_rate = self.get_exchange_rate(self.doc.currency, + company_currency) + + def get_exchange_rate(self, from_currency, to_currency): + exchange = "%s-%s" % (from_currency, to_currency) + return flt(webnotes.conn.get_value("Currency Exchange", exchange, "exchange_rate")) + def set_missing_item_details(self, get_item_details): """set missing item values""" for item in self.doclist.get({"parentfield": self.fname}): diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 1b414221e4..f1117ed177 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -23,7 +23,7 @@ class SellingController(StockController): self.set_price_list_and_item_details() if self.doc.fields.get("__islocal"): self.set_taxes("other_charges", "charge") - + def set_missing_lead_customer_details(self): if self.doc.customer: if not (self.doc.contact_person and self.doc.customer_address and self.doc.customer_name): diff --git a/controllers/stock_controller.py b/controllers/stock_controller.py index 9adefb0d0b..4734dea695 100644 --- a/controllers/stock_controller.py +++ b/controllers/stock_controller.py @@ -16,7 +16,7 @@ class StockController(AccountsController): return if self.doc.docstatus==1: - gl_entries = self.get_gl_entries_for_stock() + gl_entries = self.get_gl_entries_for_stock() make_gl_entries(gl_entries) else: delete_gl_entries(voucher_type=self.doc.doctype, voucher_no=self.doc.name) @@ -31,12 +31,12 @@ class StockController(AccountsController): default_cost_center) gl_list = [] + warehouse_with_no_account = [] for detail in voucher_details: sle_list = stock_ledger.get(detail.name) if sle_list: for sle in sle_list: if warehouse_account.get(sle.warehouse): - # from warehouse account gl_list.append(self.get_gl_dict({ "account": warehouse_account[sle.warehouse], @@ -54,6 +54,12 @@ class StockController(AccountsController): "remarks": self.doc.remarks or "Accounting Entry for Stock", "credit": sle.stock_value_difference })) + elif sle.warehouse not in warehouse_with_no_account: + warehouse_with_no_account.append(sle.warehouse) + + if warehouse_with_no_account: + msgprint(_("No accounting entries for following warehouses") + ": \n" + + "\n".join(warehouse_with_no_account)) return process_gl_map(gl_list) @@ -80,9 +86,11 @@ class StockController(AccountsController): return stock_ledger def get_warehouse_account(self): - warehouse_account = dict(webnotes.conn.sql("""select name, account from `tabWarehouse` - where ifnull(account, '') != ''""")) - + for d in webnotes.conn.sql("select name from tabWarehouse"): + webnotes.bean("Warehouse", d[0]).save() + + warehouse_account = dict(webnotes.conn.sql("""select master_name, name from tabAccount + where account_type = 'Warehouse' and ifnull(master_name, '') != ''""")) return warehouse_account def update_gl_entries_after(self): diff --git a/selling/doctype/customer/customer.py b/selling/doctype/customer/customer.py index fb3c0062a7..b11f3083d1 100644 --- a/selling/doctype/customer/customer.py +++ b/selling/doctype/customer/customer.py @@ -68,9 +68,9 @@ class DocType(TransactionBase): ac_bean.ignore_permissions = True ac_bean.insert() - msgprint("Account Head: %s created" % ac_bean.doc.name) + msgprint(_("Account Head") + ": " + ac_bean.doc.name + _(" created")) else : - msgprint("Please Select Company under which you want to create account head") + msgprint(_("Please Select Company under which you want to create account head")) def update_credit_days_limit(self): webnotes.conn.sql("""update tabAccount set credit_days = %s, credit_limit = %s diff --git a/setup/doctype/company/company.py b/setup/doctype/company/company.py index 38093e7178..1fd97bed9d 100644 --- a/setup/doctype/company/company.py +++ b/setup/doctype/company/company.py @@ -59,14 +59,12 @@ class DocType: def create_default_warehouses(self): for whname in ("Stores", "Work In Progress", "Finished Goods"): - wh = { + webnotes.bean({ "doctype":"Warehouse", "warehouse_name": whname, "company": self.doc.name, - } - wh.update({"account": "Stock In Hand - " + self.doc.abbr}) - - webnotes.bean(wh).insert() + "create_account_under": "Stock Assets - " + self.doc.abbr + }).insert() def create_default_web_page(self): if not webnotes.conn.get_value("Website Settings", None, "home_page"): @@ -116,7 +114,6 @@ class DocType: ['Securities and Deposits','Current Assets','Group','No','','Debit',self.doc.name,''], ['Earnest Money','Securities and Deposits','Ledger','No','','Debit',self.doc.name,''], ['Stock Assets','Current Assets','Group','No','','Debit',self.doc.name,''], - ['Stock In Hand','Stock Assets','Ledger','No','','Debit',self.doc.name,''], ['Tax Assets','Current Assets','Group','No','','Debit',self.doc.name,''], ['Fixed Assets','Application of Funds (Assets)','Group','No','','Debit',self.doc.name,''], ['Capital Equipments','Fixed Assets','Ledger','No','Fixed Asset Account','Debit',self.doc.name,''], @@ -289,9 +286,6 @@ class DocType: """ rec = webnotes.conn.sql("SELECT name from `tabGL Entry` where company = %s", self.doc.name) if not rec: - # delete gl entry - webnotes.conn.sql("delete from `tabGL Entry` where company = %s", self.doc.name) - #delete tabAccount webnotes.conn.sql("delete from `tabAccount` where company = %s order by lft desc, rgt desc", self.doc.name) @@ -300,6 +294,9 @@ class DocType: #delete cost center webnotes.conn.sql("delete from `tabCost Center` WHERE company = %s order by lft desc, rgt desc", self.doc.name) + if not webnotes.conn.get_value("Stock Ledger Entry", {"company": self.doc.name}): + webnotes.conn.sql("""delete from `tabWarehouse` where company=%s""", self.doc.name) + webnotes.defaults.clear_default("company", value=self.doc.name) webnotes.conn.sql("""update `tabSingles` set value="" diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py index 2b4bfa534f..7c525504ae 100644 --- a/stock/doctype/delivery_note/test_delivery_note.py +++ b/stock/doctype/delivery_note/test_delivery_note.py @@ -7,7 +7,7 @@ import unittest import webnotes import webnotes.defaults from webnotes.utils import cint -from stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries, test_records as pr_test_records +from stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries, set_perpetual_inventory, test_records as pr_test_records class TestDeliveryNote(unittest.TestCase): def _insert_purchase_receipt(self, item_code=None): @@ -41,7 +41,7 @@ class TestDeliveryNote(unittest.TestCase): def test_delivery_note_no_gl_entry(self): self.clear_stock_account_balance() - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) + set_perpetual_inventory(0) self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 0) self._insert_purchase_receipt() @@ -65,8 +65,7 @@ class TestDeliveryNote(unittest.TestCase): def test_delivery_note_gl_entry(self): self.clear_stock_account_balance() - - webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) + set_perpetual_inventory() self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 1) webnotes.conn.set_value("Item", "_Test Item", "valuation_method", "FIFO") @@ -76,8 +75,8 @@ class TestDeliveryNote(unittest.TestCase): dn.doclist[1].expense_account = "Cost of Goods Sold - _TC" dn.doclist[1].cost_center = "Main - _TC" - stock_in_hand_account = webnotes.conn.get_value("Warehouse", dn.doclist[1].warehouse, - "account") + stock_in_hand_account = webnotes.conn.get_value("Account", + {"master_name": dn.doclist[1].warehouse}) from accounts.utils import get_balance_on prev_bal = get_balance_on(stock_in_hand_account, dn.doc.posting_date) @@ -118,12 +117,11 @@ class TestDeliveryNote(unittest.TestCase): dn.cancel() self.assertFalse(get_gl_entries("Delivery Note", dn.doc.name)) - - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) + set_perpetual_inventory(0) def test_delivery_note_gl_entry_packing_item(self): self.clear_stock_account_balance() - webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) + set_perpetual_inventory() self._insert_purchase_receipt() self._insert_purchase_receipt("_Test Item Home Desktop 100") @@ -132,8 +130,8 @@ class TestDeliveryNote(unittest.TestCase): dn.doclist[1].item_code = "_Test Sales BOM Item" dn.doclist[1].qty = 1 - stock_in_hand_account = webnotes.conn.get_value("Warehouse", dn.doclist[1].warehouse, - "account") + stock_in_hand_account = webnotes.conn.get_value("Account", + {"master_name": dn.doclist[1].warehouse}) from accounts.utils import get_balance_on prev_bal = get_balance_on(stock_in_hand_account, dn.doc.posting_date) @@ -158,7 +156,7 @@ class TestDeliveryNote(unittest.TestCase): dn.cancel() self.assertFalse(get_gl_entries("Delivery Note", dn.doc.name)) - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) + set_perpetual_inventory(0) def test_serialized(self): from stock.doctype.stock_entry.test_stock_entry import make_serialized_item diff --git a/stock/doctype/purchase_receipt/test_purchase_receipt.py b/stock/doctype/purchase_receipt/test_purchase_receipt.py index 616151125a..b4c7cc6b3f 100644 --- a/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -12,8 +12,8 @@ from accounts.utils import get_stock_and_account_difference class TestPurchaseReceipt(unittest.TestCase): def test_make_purchase_invoice(self): - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) self._clear_stock_account_balance() + set_perpetual_inventory(0) from stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice pr = webnotes.bean(copy=test_records[0]).insert() @@ -33,8 +33,8 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertRaises(webnotes.ValidationError, webnotes.bean(pi).submit) def test_purchase_receipt_no_gl_entry(self): - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) self._clear_stock_account_balance() + set_perpetual_inventory(0) pr = webnotes.bean(copy=test_records[0]) pr.insert() pr.submit() @@ -53,11 +53,11 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertFalse(get_gl_entries("Purchase Receipt", pr.doc.name)) def test_purchase_receipt_gl_entry(self): - webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) - self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 1) - self._clear_stock_account_balance() + set_perpetual_inventory() + self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 1) + pr = webnotes.bean(copy=test_records[0]) pr.insert() pr.submit() @@ -66,10 +66,10 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertTrue(gl_entries) - stock_in_hand_account = webnotes.conn.get_value("Warehouse", pr.doclist[1].warehouse, - "account") - fixed_asset_account = webnotes.conn.get_value("Warehouse", pr.doclist[2].warehouse, - "account") + stock_in_hand_account = webnotes.conn.get_value("Account", + {"master_name": pr.doclist[1].warehouse}) + fixed_asset_account = webnotes.conn.get_value("Account", + {"master_name": pr.doclist[2].warehouse}) expected_values = { stock_in_hand_account: [375.0, 0.0], @@ -84,7 +84,7 @@ class TestPurchaseReceipt(unittest.TestCase): pr.cancel() self.assertFalse(get_gl_entries("Purchase Receipt", pr.doc.name)) - webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) + set_perpetual_inventory(0) def _clear_stock_account_balance(self): webnotes.conn.sql("delete from `tabStock Ledger Entry`") @@ -123,6 +123,10 @@ def get_gl_entries(voucher_type, voucher_no): from `tabGL Entry` where voucher_type=%s and voucher_no=%s order by account desc""", (voucher_type, voucher_no), as_dict=1) +def set_perpetual_inventory(enable=1): + accounts_settings = webnotes.bean("Accounts Settings") + accounts_settings.doc.auto_accounting_for_stock = enable + accounts_settings.save() test_dependencies = ["BOM"] diff --git a/stock/doctype/warehouse/test_warehouse.py b/stock/doctype/warehouse/test_warehouse.py index 40a8ff21c1..76b18180e2 100644 --- a/stock/doctype/warehouse/test_warehouse.py +++ b/stock/doctype/warehouse/test_warehouse.py @@ -6,18 +6,18 @@ test_records = [ "doctype": "Warehouse", "warehouse_name": "_Test Warehouse", "company": "_Test Company", - "account": "_Test Account Stock In Hand - _TC" + "create_account_under": "Stock Assets - _TC" }], [{ "doctype": "Warehouse", "warehouse_name": "_Test Warehouse 1", "company": "_Test Company", - "account": "_Test Account Fixed Assets - _TC" + "create_account_under": "Fixed Assets - _TC" }], [{ "doctype": "Warehouse", "warehouse_name": "_Test Warehouse 2", - "account": "_Test Account Stock In Hand - _TC1", + "create_account_under": "Stock Assets - _TC", "company": "_Test Company 1" }, { "doctype": "Warehouse User", diff --git a/stock/doctype/warehouse/warehouse.js b/stock/doctype/warehouse/warehouse.js index 34745f5346..2a58a387f8 100644 --- a/stock/doctype/warehouse/warehouse.js +++ b/stock/doctype/warehouse/warehouse.js @@ -17,12 +17,12 @@ cur_frm.cscript.merge = function(doc, cdt, cdn) { } } -cur_frm.set_query("account", function() { +cur_frm.set_query("create_account_under", function() { return { filters: { "company": cur_frm.doc.company, "debit_or_credit": "Debit", - 'group_or_ledger': "Ledger" + 'group_or_ledger': "Group" } } }) diff --git a/stock/doctype/warehouse/warehouse.py b/stock/doctype/warehouse/warehouse.py index 6a532ffaf0..eb84acf1e6 100644 --- a/stock/doctype/warehouse/warehouse.py +++ b/stock/doctype/warehouse/warehouse.py @@ -6,7 +6,7 @@ import webnotes from webnotes.utils import cint, flt, validate_email_add from webnotes.model.code import get_obj -from webnotes import msgprint +from webnotes import msgprint, _ sql = webnotes.conn.sql @@ -23,21 +23,47 @@ class DocType: def validate(self): if self.doc.email_id and not validate_email_add(self.doc.email_id): msgprint("Please enter valid Email Id", raise_exception=1) - - self.account_mandatory() - def account_mandatory(self): - if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")): - sle_exists = webnotes.conn.get_value("Stock Ledger Entry", {"warehouse": self.doc.name}) - if not self.doc.account and (self.doc.__islocal or not sle_exists): - webnotes.throw(_("Asset/Expense Account mandatory")) + self.validate_parent_account() - if not self.doc.__islocal and sle_exists: - old_account = webnotes.conn.get_value("Warehouse", self.doc.name, "account") - if old_account != self.doc.account: - webnotes.throw(_("Account can not be changed/assigned/removed as \ - stock transactions exist for this warehouse")) + def on_update(self): + self.create_account_head() + def create_account_head(self): + if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")): + if not webnotes.conn.get_value("Account", {"account_type": "Warehouse", + "master_name": self.doc.name}): + if self.doc.__islocal or not webnotes.conn.get_value("Stock Ledger Entry", + {"warehouse": self.doc.name}): + ac_bean = webnotes.bean({ + "doctype": "Account", + 'account_name': self.doc.warehouse_name, + 'parent_account': self.doc.create_account_under, + 'group_or_ledger':'Ledger', + 'company':self.doc.company, + "account_type": "Warehouse", + "master_name": self.doc.name, + "freeze_account": "No" + }) + ac_bean.ignore_permissions = True + ac_bean.insert() + + msgprint(_("Account Head") + ": " + ac_bean.doc.name + _(" created")) + + def validate_parent_account(self): + if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")) and \ + not self.doc.create_account_under: + parent_account = webnotes.conn.get_value("Account", + {"account_name": "Stock Assets", "company": self.doc.company}) + if parent_account: + self.doc.create_account_under = parent_account + else: + webnotes.throw(_("Please enter account group under which account \ + for warehouse ") + self.doc.name +_(" will be created")) + + def on_rename(self, new, old): + webnotes.conn.set_value("Account", {"account_type": "Warehouse", "master_name": old}, + "master_name", new) def merge_warehouses(self): webnotes.conn.auto_commit_on_many_writes = 1 @@ -52,6 +78,15 @@ class DocType: link_fields = rename_doc.get_link_fields('Warehouse') rename_doc.update_link_field_values(link_fields, self.doc.name, self.doc.merge_with) + account_link_fields = rename_doc.get_link_fields('Account') + old_warehouse_account = webnotes.conn.get_value("Account", {"master_name": self.doc.name}) + new_warehouse_account = webnotes.conn.get_value("Account", + {"master_name": self.doc.merge_with}) + rename_doc.update_link_field_values(account_link_fields, old_warehouse_account, + new_warehouse_account) + + webnotes.conn.delete_doc("Account", old_warehouse_account) + for item_code in items: self.repost(item_code[0], self.doc.merge_with) @@ -160,6 +195,11 @@ class DocType: else: sql("delete from `tabBin` where name = %s", d['name']) + warehouse_account = webnotes.conn.get_value("Account", + {"account_type": "Warehosue", "master_name": self.doc.name}) + if warehouse_account: + webnotes.delete_doc("Account", warehouse_account) + # delete cancelled sle if sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.doc.name): msgprint("""Warehosue can not be deleted as stock ledger entry diff --git a/stock/doctype/warehouse/warehouse.txt b/stock/doctype/warehouse/warehouse.txt index 1b1fc33774..76ddac7930 100644 --- a/stock/doctype/warehouse/warehouse.txt +++ b/stock/doctype/warehouse/warehouse.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 18:50:32", "docstatus": 0, - "modified": "2013-08-01 15:27:49", + "modified": "2013-09-16 10:45:49", "modified_by": "Administrator", "owner": "Administrator" }, @@ -28,9 +28,9 @@ "parent": "Warehouse", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1, - "report": 1, - "submit": 0 + "report": 1 }, { "doctype": "DocType", @@ -71,11 +71,12 @@ "search_index": 1 }, { - "description": "This account will be used for perpetual accounting for inventory e.g. Stock-in-Hand, Fixed Asset Account etc", + "depends_on": "eval:sys_defaults.auto_accounting_for_stock", + "description": "Account for the warehouse (Perpetual Inventory) will be created under this Account.", "doctype": "DocField", - "fieldname": "account", + "fieldname": "create_account_under", "fieldtype": "Link", - "label": "Account", + "label": "Create Account Under", "options": "Account", "permlevel": 0 }, @@ -231,16 +232,8 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, "role": "Material Master Manager", - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "permlevel": 0, - "role": "System Manager", + "submit": 0, "write": 1 }, { @@ -248,26 +241,20 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "permlevel": 0, - "role": "Material Manager", - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "permlevel": 0, "role": "Material User", + "submit": 0, "write": 0 }, { - "amend": 0, - "cancel": 0, - "create": 0, "doctype": "DocPerm", - "permlevel": 2, - "role": "System Manager", - "write": 1 + "role": "Sales User" + }, + { + "doctype": "DocPerm", + "role": "Purchase User" + }, + { + "doctype": "DocPerm", + "role": "Accounts User" } ] \ No newline at end of file