[fix] [minor] perpetual inventory: account for each warehouse

This commit is contained in:
Nabin Hait 2013-09-17 10:21:20 +05:30
parent 87eb4b99a8
commit 7a75e10a61
18 changed files with 241 additions and 171 deletions

View File

@ -32,10 +32,10 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
} else { } else {
// credit days and type if customer or supplier // credit days and type if customer or supplier
cur_frm.set_intro(null); cur_frm.set_intro(null);
cur_frm.toggle_display(['credit_days', 'credit_limit', 'master_name'], cur_frm.toggle_display(['credit_days', 'credit_limit'], in_list(['Customer', 'Supplier'],
in_list(['Customer', 'Supplier'], doc.master_type)); doc.master_type));
// hide tax_rate cur_frm.cscript.master_type(doc, cdt, cdn);
cur_frm.cscript.account_type(doc, cdt, cdn); cur_frm.cscript.account_type(doc, cdt, cdn);
// show / hide convert buttons // 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.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)); 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) { cur_frm.cscript.account_type = function(doc, cdt, cdn) {
if(doc.group_or_ledger=='Ledger') { if(doc.group_or_ledger=='Ledger') {
cur_frm.toggle_display(['tax_rate'], cur_frm.toggle_display(['tax_rate'], doc.account_type == 'Tax');
doc.account_type == 'Tax'); cur_frm.toggle_display('master_type', cstr(doc.account_type)=='');
cur_frm.toggle_display(['master_type', 'master_name'], cur_frm.toggle_display('master_name', doc.account_type=='Warehouse' ||
cstr(doc.account_type)==''); 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) { 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 { return {
doctype: doc.master_type, doctype: dt,
query: "accounts.doctype.account.account.get_master_name", query: "accounts.doctype.account.account.get_master_name",
filters: { "master_type": doc.master_type } filters: {
"master_type": dt,
"company": doc.company
}
} }
} }
} }

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import flt, fmt_money from webnotes.utils import flt, fmt_money, cstr, cint
from webnotes import msgprint, _ from webnotes import msgprint, _
sql = webnotes.conn.sql sql = webnotes.conn.sql
@ -16,13 +16,25 @@ class DocType:
self.nsm_parent_field = 'parent_account' self.nsm_parent_field = 'parent_account'
def autoname(self): def autoname(self):
"""Append abbreviation to company on naming"""
self.doc.name = self.doc.account_name.strip() + ' - ' + \ self.doc.name = self.doc.account_name.strip() + ' - ' + \
webnotes.conn.get_value("Company", self.doc.company, "abbr") webnotes.conn.get_value("Company", self.doc.company, "abbr")
def get_address(self): def get_address(self):
address = webnotes.conn.get_value(self.doc.master_type, self.doc.master_name, "address") return {
return {'address': address} '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): def validate_master_name(self):
"""Remind to add master name""" """Remind to add master name"""
@ -109,16 +121,26 @@ class DocType:
msgprint("Debit or Credit field is mandatory", raise_exception=1) msgprint("Debit or Credit field is mandatory", raise_exception=1)
if not self.doc.is_pl_account: if not self.doc.is_pl_account:
msgprint("Is PL Account field is mandatory", raise_exception=1) 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): def update_nsm_model(self):
"""update lft, rgt indices for nested set model""" """update lft, rgt indices for nested set model"""
@ -198,9 +220,11 @@ class DocType:
return " - ".join(parts) return " - ".join(parts)
def get_master_name(doctype, txt, searchfield, start, page_len, filters): 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""" % 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) ("%%%s%%" % txt, start, page_len), as_list=1)
def get_parent_account(doctype, txt, searchfield, start, page_len, filters): def get_parent_account(doctype, txt, searchfield, start, page_len, filters):

View File

@ -2,7 +2,7 @@
{ {
"creation": "2013-01-30 12:49:46", "creation": "2013-01-30 12:49:46",
"docstatus": 0, "docstatus": 0,
"modified": "2013-07-05 14:23:30", "modified": "2013-09-16 12:21:10",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -162,7 +162,7 @@
"label": "Account Type", "label": "Account Type",
"oldfieldname": "account_type", "oldfieldname": "account_type",
"oldfieldtype": "Select", "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, "permlevel": 0,
"search_index": 0 "search_index": 0
}, },

View File

@ -13,5 +13,9 @@ class DocType:
self.doc, self.doclist = d, dl self.doc, self.doclist = d, dl
def on_update(self): def on_update(self):
for key in ["auto_accounting_for_stock"]: webnotes.conn.set_default("auto_accounting_for_stock", self.doc.auto_accounting_for_stock)
webnotes.conn.set_default(key, self.doc.fields.get(key, ''))
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()

View File

@ -2,7 +2,7 @@
{ {
"creation": "2013-06-24 15:49:57", "creation": "2013-06-24 15:49:57",
"docstatus": 0, "docstatus": 0,
"modified": "2013-08-28 18:55:43", "modified": "2013-09-16 14:24:39",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -44,7 +44,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "auto_accounting_for_stock", "fieldname": "auto_accounting_for_stock",
"fieldtype": "Check", "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.", "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",

View File

@ -192,8 +192,6 @@ class DocType(SellingController):
pos = get_pos_settings(self.doc.company) pos = get_pos_settings(self.doc.company)
if pos: if pos:
self.doc.conversion_rate = flt(pos.conversion_rate)
if not for_validate: if not for_validate:
self.doc.customer = pos.customer self.doc.customer = pos.customer
self.set_customer_defaults() self.set_customer_defaults()

View File

@ -6,10 +6,12 @@ import unittest, json
from webnotes.utils import flt, cint from webnotes.utils import flt, cint
from webnotes.model.bean import DocstatusTransitionError, TimestampMismatchError from webnotes.model.bean import DocstatusTransitionError, TimestampMismatchError
from accounts.utils import get_stock_and_account_difference 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): class TestSalesInvoice(unittest.TestCase):
def make(self): def make(self):
w = webnotes.bean(copy=test_records[0]) w = webnotes.bean(copy=test_records[0])
w.doc.is_pos = 0
w.insert() w.insert()
w.submit() w.submit()
return w return w
@ -93,7 +95,6 @@ class TestSalesInvoice(unittest.TestCase):
si.doclist[1].ref_rate = 1 si.doclist[1].ref_rate = 1
si.doclist[2].export_rate = 3 si.doclist[2].export_rate = 3
si.doclist[2].ref_rate = 3 si.doclist[2].ref_rate = 3
si.run_method("calculate_taxes_and_totals")
si.insert() si.insert()
expected_values = { expected_values = {
@ -259,6 +260,7 @@ class TestSalesInvoice(unittest.TestCase):
def test_payment(self): def test_payment(self):
w = self.make() w = self.make()
from accounts.doctype.journal_voucher.test_journal_voucher \ from accounts.doctype.journal_voucher.test_journal_voucher \
import test_records as jv_test_records import test_records as jv_test_records
@ -298,8 +300,8 @@ class TestSalesInvoice(unittest.TestCase):
"Batched for Billing") "Batched for Billing")
def test_sales_invoice_gl_entry_without_aii(self): def test_sales_invoice_gl_entry_without_aii(self):
webnotes.defaults.set_global_default("auto_accounting_for_stock", 0)
self.clear_stock_account_balance() self.clear_stock_account_balance()
set_perpetual_inventory(0)
si = webnotes.bean(copy=test_records[1]) si = webnotes.bean(copy=test_records[1])
si.insert() si.insert()
si.submit() si.submit()
@ -331,10 +333,8 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle) self.assertFalse(gle)
def test_pos_gl_entry_with_aii(self): def test_pos_gl_entry_with_aii(self):
webnotes.conn.sql("delete from `tabStock Ledger Entry`") self.clear_stock_account_balance()
webnotes.conn.sql("delete from `tabGL Entry`") set_perpetual_inventory()
webnotes.conn.sql("delete from `tabBin`")
webnotes.defaults.set_global_default("auto_accounting_for_stock", 1)
self._insert_purchase_receipt() self._insert_purchase_receipt()
self._insert_pos_settings() self._insert_pos_settings()
@ -359,18 +359,19 @@ class TestSalesInvoice(unittest.TestCase):
["_Test Item", "_Test Warehouse - _TC", -1.0]) ["_Test Item", "_Test Warehouse - _TC", -1.0])
# check gl entries # check gl entries
gl_entries = webnotes.conn.sql("""select account, debit, credit gl_entries = webnotes.conn.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc, debit asc""", si.doc.name, as_dict=1) order by account asc, debit asc""", si.doc.name, as_dict=1)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
stock_in_hand = webnotes.conn.get_value("Account", {"master_name": "_Test Warehouse - _TC"})
expected_gl_entries = sorted([ expected_gl_entries = sorted([
[si.doc.debit_to, 630.0, 0.0], [si.doc.debit_to, 630.0, 0.0],
[pos[1]["income_account"], 0.0, 500.0], [pos[1]["income_account"], 0.0, 500.0],
[pos[2]["account_head"], 0.0, 80.0], [pos[2]["account_head"], 0.0, 80.0],
[pos[3]["account_head"], 0.0, 50.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], [pos[1]["expense_account"], 75.0, 0.0],
[si.doc.debit_to, 0.0, 600.0], [si.doc.debit_to, 0.0, 600.0],
["_Test Account Bank Account - _TC", 600.0, 0.0] ["_Test Account Bank Account - _TC", 600.0, 0.0]
@ -389,12 +390,13 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle) 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): def test_sales_invoice_gl_entry_with_aii_no_item_code(self):
webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) self.clear_stock_account_balance()
set_perpetual_inventory()
si_copy = webnotes.copy_doclist(test_records[1]) si_copy = webnotes.copy_doclist(test_records[1])
si_copy[1]["item_code"] = None 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][0], gle.account)
self.assertEquals(expected_values[i][1], gle.debit) self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit) 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 = webnotes.copy_doclist(test_records[1])
si_copy[1]["item_code"] = "_Test Non Stock Item" si_copy[1]["item_code"] = "_Test Non Stock Item"
si = webnotes.bean(si_copy) 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][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit) 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): def _insert_purchase_receipt(self):
from stock.doctype.purchase_receipt.test_purchase_receipt import test_records \ from stock.doctype.purchase_receipt.test_purchase_receipt import test_records \

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import webnotes import webnotes
from webnotes import _, msgprint from webnotes import _, msgprint
from webnotes.utils import flt, cint, today, cstr 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 accounts.utils import get_fiscal_year, validate_fiscal_year
from utilities.transaction_base import TransactionBase, validate_conversion_rate from utilities.transaction_base import TransactionBase, validate_conversion_rate
import json import json
@ -13,7 +13,6 @@ import json
class AccountsController(TransactionBase): class AccountsController(TransactionBase):
def validate(self): def validate(self):
self.set_missing_values(for_validate=True) self.set_missing_values(for_validate=True)
self.validate_date_with_fiscal_year() self.validate_date_with_fiscal_year()
if self.meta.get_field("currency"): if self.meta.get_field("currency"):
self.calculate_taxes_and_totals() self.calculate_taxes_and_totals()
@ -54,35 +53,37 @@ class AccountsController(TransactionBase):
self.doc.doctype + _(" can not be made."), raise_exception=1) self.doc.doctype + _(" can not be made."), raise_exception=1)
def set_price_list_currency(self, buying_or_selling): 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.meta.get_field("currency"):
if self.doc.currency and self.doc.currency != company_currency: company_currency = get_company_currency(self.doc.company)
if not self.doc.conversion_rate:
exchange = self.doc.currency + "-" + company_currency # price list part
self.doc.conversion_rate = flt(webnotes.conn.get_value("Currency Exchange", fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \
exchange, "exchange_rate")) else "buying_price_list"
else: if self.meta.get_field(fieldname) and self.doc.fields.get(fieldname):
self.doc.conversion_rate = 1 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): def set_missing_item_details(self, get_item_details):
"""set missing item values""" """set missing item values"""
for item in self.doclist.get({"parentfield": self.fname}): for item in self.doclist.get({"parentfield": self.fname}):

View File

@ -23,7 +23,7 @@ class SellingController(StockController):
self.set_price_list_and_item_details() self.set_price_list_and_item_details()
if self.doc.fields.get("__islocal"): if self.doc.fields.get("__islocal"):
self.set_taxes("other_charges", "charge") self.set_taxes("other_charges", "charge")
def set_missing_lead_customer_details(self): def set_missing_lead_customer_details(self):
if self.doc.customer: if self.doc.customer:
if not (self.doc.contact_person and self.doc.customer_address and self.doc.customer_name): if not (self.doc.contact_person and self.doc.customer_address and self.doc.customer_name):

View File

@ -16,7 +16,7 @@ class StockController(AccountsController):
return return
if self.doc.docstatus==1: 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) make_gl_entries(gl_entries)
else: else:
delete_gl_entries(voucher_type=self.doc.doctype, voucher_no=self.doc.name) delete_gl_entries(voucher_type=self.doc.doctype, voucher_no=self.doc.name)
@ -31,12 +31,12 @@ class StockController(AccountsController):
default_cost_center) default_cost_center)
gl_list = [] gl_list = []
warehouse_with_no_account = []
for detail in voucher_details: for detail in voucher_details:
sle_list = stock_ledger.get(detail.name) sle_list = stock_ledger.get(detail.name)
if sle_list: if sle_list:
for sle in sle_list: for sle in sle_list:
if warehouse_account.get(sle.warehouse): if warehouse_account.get(sle.warehouse):
# from warehouse account # from warehouse account
gl_list.append(self.get_gl_dict({ gl_list.append(self.get_gl_dict({
"account": warehouse_account[sle.warehouse], "account": warehouse_account[sle.warehouse],
@ -54,6 +54,12 @@ class StockController(AccountsController):
"remarks": self.doc.remarks or "Accounting Entry for Stock", "remarks": self.doc.remarks or "Accounting Entry for Stock",
"credit": sle.stock_value_difference "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) return process_gl_map(gl_list)
@ -80,9 +86,11 @@ class StockController(AccountsController):
return stock_ledger return stock_ledger
def get_warehouse_account(self): def get_warehouse_account(self):
warehouse_account = dict(webnotes.conn.sql("""select name, account from `tabWarehouse` for d in webnotes.conn.sql("select name from tabWarehouse"):
where ifnull(account, '') != ''""")) 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 return warehouse_account
def update_gl_entries_after(self): def update_gl_entries_after(self):

View File

@ -68,9 +68,9 @@ class DocType(TransactionBase):
ac_bean.ignore_permissions = True ac_bean.ignore_permissions = True
ac_bean.insert() ac_bean.insert()
msgprint("Account Head: %s created" % ac_bean.doc.name) msgprint(_("Account Head") + ": " + ac_bean.doc.name + _(" created"))
else : 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): def update_credit_days_limit(self):
webnotes.conn.sql("""update tabAccount set credit_days = %s, credit_limit = %s webnotes.conn.sql("""update tabAccount set credit_days = %s, credit_limit = %s

View File

@ -59,14 +59,12 @@ class DocType:
def create_default_warehouses(self): def create_default_warehouses(self):
for whname in ("Stores", "Work In Progress", "Finished Goods"): for whname in ("Stores", "Work In Progress", "Finished Goods"):
wh = { webnotes.bean({
"doctype":"Warehouse", "doctype":"Warehouse",
"warehouse_name": whname, "warehouse_name": whname,
"company": self.doc.name, "company": self.doc.name,
} "create_account_under": "Stock Assets - " + self.doc.abbr
wh.update({"account": "Stock In Hand - " + self.doc.abbr}) }).insert()
webnotes.bean(wh).insert()
def create_default_web_page(self): def create_default_web_page(self):
if not webnotes.conn.get_value("Website Settings", None, "home_page"): 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,''], ['Securities and Deposits','Current Assets','Group','No','','Debit',self.doc.name,''],
['Earnest Money','Securities and Deposits','Ledger','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 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,''], ['Tax Assets','Current Assets','Group','No','','Debit',self.doc.name,''],
['Fixed Assets','Application of Funds (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,''], ['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) rec = webnotes.conn.sql("SELECT name from `tabGL Entry` where company = %s", self.doc.name)
if not rec: if not rec:
# delete gl entry
webnotes.conn.sql("delete from `tabGL Entry` where company = %s", self.doc.name)
#delete tabAccount #delete tabAccount
webnotes.conn.sql("delete from `tabAccount` where company = %s order by lft desc, rgt desc", self.doc.name) 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 #delete cost center
webnotes.conn.sql("delete from `tabCost Center` WHERE company = %s order by lft desc, rgt desc", self.doc.name) 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.defaults.clear_default("company", value=self.doc.name)
webnotes.conn.sql("""update `tabSingles` set value="" webnotes.conn.sql("""update `tabSingles` set value=""

View File

@ -7,7 +7,7 @@ import unittest
import webnotes import webnotes
import webnotes.defaults import webnotes.defaults
from webnotes.utils import cint 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): class TestDeliveryNote(unittest.TestCase):
def _insert_purchase_receipt(self, item_code=None): def _insert_purchase_receipt(self, item_code=None):
@ -41,7 +41,7 @@ class TestDeliveryNote(unittest.TestCase):
def test_delivery_note_no_gl_entry(self): def test_delivery_note_no_gl_entry(self):
self.clear_stock_account_balance() 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.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 0)
self._insert_purchase_receipt() self._insert_purchase_receipt()
@ -65,8 +65,7 @@ class TestDeliveryNote(unittest.TestCase):
def test_delivery_note_gl_entry(self): def test_delivery_note_gl_entry(self):
self.clear_stock_account_balance() self.clear_stock_account_balance()
set_perpetual_inventory()
webnotes.defaults.set_global_default("auto_accounting_for_stock", 1)
self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 1) self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 1)
webnotes.conn.set_value("Item", "_Test Item", "valuation_method", "FIFO") 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].expense_account = "Cost of Goods Sold - _TC"
dn.doclist[1].cost_center = "Main - _TC" dn.doclist[1].cost_center = "Main - _TC"
stock_in_hand_account = webnotes.conn.get_value("Warehouse", dn.doclist[1].warehouse, stock_in_hand_account = webnotes.conn.get_value("Account",
"account") {"master_name": dn.doclist[1].warehouse})
from accounts.utils import get_balance_on from accounts.utils import get_balance_on
prev_bal = get_balance_on(stock_in_hand_account, dn.doc.posting_date) prev_bal = get_balance_on(stock_in_hand_account, dn.doc.posting_date)
@ -118,12 +117,11 @@ class TestDeliveryNote(unittest.TestCase):
dn.cancel() dn.cancel()
self.assertFalse(get_gl_entries("Delivery Note", dn.doc.name)) self.assertFalse(get_gl_entries("Delivery Note", dn.doc.name))
set_perpetual_inventory(0)
webnotes.defaults.set_global_default("auto_accounting_for_stock", 0)
def test_delivery_note_gl_entry_packing_item(self): def test_delivery_note_gl_entry_packing_item(self):
self.clear_stock_account_balance() 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()
self._insert_purchase_receipt("_Test Item Home Desktop 100") 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].item_code = "_Test Sales BOM Item"
dn.doclist[1].qty = 1 dn.doclist[1].qty = 1
stock_in_hand_account = webnotes.conn.get_value("Warehouse", dn.doclist[1].warehouse, stock_in_hand_account = webnotes.conn.get_value("Account",
"account") {"master_name": dn.doclist[1].warehouse})
from accounts.utils import get_balance_on from accounts.utils import get_balance_on
prev_bal = get_balance_on(stock_in_hand_account, dn.doc.posting_date) prev_bal = get_balance_on(stock_in_hand_account, dn.doc.posting_date)
@ -158,7 +156,7 @@ class TestDeliveryNote(unittest.TestCase):
dn.cancel() dn.cancel()
self.assertFalse(get_gl_entries("Delivery Note", dn.doc.name)) 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): def test_serialized(self):
from stock.doctype.stock_entry.test_stock_entry import make_serialized_item from stock.doctype.stock_entry.test_stock_entry import make_serialized_item

View File

@ -12,8 +12,8 @@ from accounts.utils import get_stock_and_account_difference
class TestPurchaseReceipt(unittest.TestCase): class TestPurchaseReceipt(unittest.TestCase):
def test_make_purchase_invoice(self): def test_make_purchase_invoice(self):
webnotes.defaults.set_global_default("auto_accounting_for_stock", 0)
self._clear_stock_account_balance() self._clear_stock_account_balance()
set_perpetual_inventory(0)
from stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice from stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
pr = webnotes.bean(copy=test_records[0]).insert() pr = webnotes.bean(copy=test_records[0]).insert()
@ -33,8 +33,8 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertRaises(webnotes.ValidationError, webnotes.bean(pi).submit) self.assertRaises(webnotes.ValidationError, webnotes.bean(pi).submit)
def test_purchase_receipt_no_gl_entry(self): def test_purchase_receipt_no_gl_entry(self):
webnotes.defaults.set_global_default("auto_accounting_for_stock", 0)
self._clear_stock_account_balance() self._clear_stock_account_balance()
set_perpetual_inventory(0)
pr = webnotes.bean(copy=test_records[0]) pr = webnotes.bean(copy=test_records[0])
pr.insert() pr.insert()
pr.submit() pr.submit()
@ -53,11 +53,11 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertFalse(get_gl_entries("Purchase Receipt", pr.doc.name)) self.assertFalse(get_gl_entries("Purchase Receipt", pr.doc.name))
def test_purchase_receipt_gl_entry(self): 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() 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 = webnotes.bean(copy=test_records[0])
pr.insert() pr.insert()
pr.submit() pr.submit()
@ -66,10 +66,10 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
stock_in_hand_account = webnotes.conn.get_value("Warehouse", pr.doclist[1].warehouse, stock_in_hand_account = webnotes.conn.get_value("Account",
"account") {"master_name": pr.doclist[1].warehouse})
fixed_asset_account = webnotes.conn.get_value("Warehouse", pr.doclist[2].warehouse, fixed_asset_account = webnotes.conn.get_value("Account",
"account") {"master_name": pr.doclist[2].warehouse})
expected_values = { expected_values = {
stock_in_hand_account: [375.0, 0.0], stock_in_hand_account: [375.0, 0.0],
@ -84,7 +84,7 @@ class TestPurchaseReceipt(unittest.TestCase):
pr.cancel() pr.cancel()
self.assertFalse(get_gl_entries("Purchase Receipt", pr.doc.name)) 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): def _clear_stock_account_balance(self):
webnotes.conn.sql("delete from `tabStock Ledger Entry`") 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 from `tabGL Entry` where voucher_type=%s and voucher_no=%s
order by account desc""", (voucher_type, voucher_no), as_dict=1) 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"] test_dependencies = ["BOM"]

View File

@ -6,18 +6,18 @@ test_records = [
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Warehouse", "warehouse_name": "_Test Warehouse",
"company": "_Test Company", "company": "_Test Company",
"account": "_Test Account Stock In Hand - _TC" "create_account_under": "Stock Assets - _TC"
}], }],
[{ [{
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Warehouse 1", "warehouse_name": "_Test Warehouse 1",
"company": "_Test Company", "company": "_Test Company",
"account": "_Test Account Fixed Assets - _TC" "create_account_under": "Fixed Assets - _TC"
}], }],
[{ [{
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Warehouse 2", "warehouse_name": "_Test Warehouse 2",
"account": "_Test Account Stock In Hand - _TC1", "create_account_under": "Stock Assets - _TC",
"company": "_Test Company 1" "company": "_Test Company 1"
}, { }, {
"doctype": "Warehouse User", "doctype": "Warehouse User",

View File

@ -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 { return {
filters: { filters: {
"company": cur_frm.doc.company, "company": cur_frm.doc.company,
"debit_or_credit": "Debit", "debit_or_credit": "Debit",
'group_or_ledger': "Ledger" 'group_or_ledger': "Group"
} }
} }
}) })

View File

@ -6,7 +6,7 @@ import webnotes
from webnotes.utils import cint, flt, validate_email_add from webnotes.utils import cint, flt, validate_email_add
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
from webnotes import msgprint from webnotes import msgprint, _
sql = webnotes.conn.sql sql = webnotes.conn.sql
@ -23,21 +23,47 @@ class DocType:
def validate(self): def validate(self):
if self.doc.email_id and not validate_email_add(self.doc.email_id): if self.doc.email_id and not validate_email_add(self.doc.email_id):
msgprint("Please enter valid Email Id", raise_exception=1) msgprint("Please enter valid Email Id", raise_exception=1)
self.account_mandatory()
def account_mandatory(self): self.validate_parent_account()
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"))
if not self.doc.__islocal and sle_exists: def on_update(self):
old_account = webnotes.conn.get_value("Warehouse", self.doc.name, "account") self.create_account_head()
if old_account != self.doc.account:
webnotes.throw(_("Account can not be changed/assigned/removed as \
stock transactions exist for this warehouse"))
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): def merge_warehouses(self):
webnotes.conn.auto_commit_on_many_writes = 1 webnotes.conn.auto_commit_on_many_writes = 1
@ -52,6 +78,15 @@ class DocType:
link_fields = rename_doc.get_link_fields('Warehouse') link_fields = rename_doc.get_link_fields('Warehouse')
rename_doc.update_link_field_values(link_fields, self.doc.name, self.doc.merge_with) 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: for item_code in items:
self.repost(item_code[0], self.doc.merge_with) self.repost(item_code[0], self.doc.merge_with)
@ -160,6 +195,11 @@ class DocType:
else: else:
sql("delete from `tabBin` where name = %s", d['name']) 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 # delete cancelled sle
if sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.doc.name): if sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.doc.name):
msgprint("""Warehosue can not be deleted as stock ledger entry msgprint("""Warehosue can not be deleted as stock ledger entry

View File

@ -2,7 +2,7 @@
{ {
"creation": "2013-03-07 18:50:32", "creation": "2013-03-07 18:50:32",
"docstatus": 0, "docstatus": 0,
"modified": "2013-08-01 15:27:49", "modified": "2013-09-16 10:45:49",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -28,9 +28,9 @@
"parent": "Warehouse", "parent": "Warehouse",
"parentfield": "permissions", "parentfield": "permissions",
"parenttype": "DocType", "parenttype": "DocType",
"permlevel": 0,
"read": 1, "read": 1,
"report": 1, "report": 1
"submit": 0
}, },
{ {
"doctype": "DocType", "doctype": "DocType",
@ -71,11 +71,12 @@
"search_index": 1 "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", "doctype": "DocField",
"fieldname": "account", "fieldname": "create_account_under",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Account", "label": "Create Account Under",
"options": "Account", "options": "Account",
"permlevel": 0 "permlevel": 0
}, },
@ -231,16 +232,8 @@
"cancel": 1, "cancel": 1,
"create": 1, "create": 1,
"doctype": "DocPerm", "doctype": "DocPerm",
"permlevel": 0,
"role": "Material Master Manager", "role": "Material Master Manager",
"write": 1 "submit": 0,
},
{
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
"permlevel": 0,
"role": "System Manager",
"write": 1 "write": 1
}, },
{ {
@ -248,26 +241,20 @@
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"doctype": "DocPerm", "doctype": "DocPerm",
"permlevel": 0,
"role": "Material Manager",
"write": 0
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
"permlevel": 0,
"role": "Material User", "role": "Material User",
"submit": 0,
"write": 0 "write": 0
}, },
{ {
"amend": 0,
"cancel": 0,
"create": 0,
"doctype": "DocPerm", "doctype": "DocPerm",
"permlevel": 2, "role": "Sales User"
"role": "System Manager", },
"write": 1 {
"doctype": "DocPerm",
"role": "Purchase User"
},
{
"doctype": "DocPerm",
"role": "Accounts User"
} }
] ]