mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-02 03:39:11 +00:00
[perpetual accounting] gl entries based on stock_value difference in sl entries
This commit is contained in:
@@ -9,40 +9,38 @@ def make_test_records(verbose):
|
||||
|
||||
accounts = [
|
||||
# [account_name, parent_account, group_or_ledger]
|
||||
["_Test Account Bank Account", "Bank Accounts - _TC", "Ledger"],
|
||||
["_Test Account Bank Account", "Bank Accounts", "Ledger"],
|
||||
|
||||
["_Test Account Stock Expenses", "Direct Expenses - _TC", "Group"],
|
||||
["_Test Account Shipping Charges", "_Test Account Stock Expenses - _TC", "Ledger"],
|
||||
["_Test Account Customs Duty", "_Test Account Stock Expenses - _TC", "Ledger"],
|
||||
["_Test Account Stock Expenses", "Direct Expenses", "Group"],
|
||||
["_Test Account Shipping Charges", "_Test Account Stock Expenses", "Ledger"],
|
||||
["_Test Account Customs Duty", "_Test Account Stock Expenses", "Ledger"],
|
||||
|
||||
|
||||
["_Test Account Tax Assets", "Current Assets - _TC", "Group"],
|
||||
["_Test Account VAT", "_Test Account Tax Assets - _TC", "Ledger"],
|
||||
["_Test Account Service Tax", "_Test Account Tax Assets - _TC", "Ledger"],
|
||||
["_Test Account Tax Assets", "Current Assets", "Group"],
|
||||
["_Test Account VAT", "_Test Account Tax Assets", "Ledger"],
|
||||
["_Test Account Service Tax", "_Test Account Tax Assets", "Ledger"],
|
||||
|
||||
["_Test Account Reserves and Surplus", "Current Liabilities - _TC", "Ledger"],
|
||||
["_Test Account Reserves and Surplus", "Current Liabilities", "Ledger"],
|
||||
|
||||
["_Test Account Cost for Goods Sold", "Expenses - _TC", "Ledger"],
|
||||
["_Test Account Excise Duty", "_Test Account Tax Assets - _TC", "Ledger"],
|
||||
["_Test Account Education Cess", "_Test Account Tax Assets - _TC", "Ledger"],
|
||||
["_Test Account S&H Education Cess", "_Test Account Tax Assets - _TC", "Ledger"],
|
||||
["_Test Account CST", "Direct Expenses - _TC", "Ledger"],
|
||||
["_Test Account Discount", "Direct Expenses - _TC", "Ledger"],
|
||||
["_Test Account Cost for Goods Sold", "Expenses", "Ledger"],
|
||||
["_Test Account Excise Duty", "_Test Account Tax Assets", "Ledger"],
|
||||
["_Test Account Education Cess", "_Test Account Tax Assets", "Ledger"],
|
||||
["_Test Account S&H Education Cess", "_Test Account Tax Assets", "Ledger"],
|
||||
["_Test Account CST", "Direct Expenses", "Ledger"],
|
||||
["_Test Account Discount", "Direct Expenses", "Ledger"],
|
||||
|
||||
# related to Account Inventory Integration
|
||||
["_Test Account Stock In Hand", "Current Assets - _TC", "Ledger"],
|
||||
["_Test Account Fixed Assets", "Current Assets - _TC", "Ledger"],
|
||||
["_Test Account Stock In Hand", "Current Assets", "Ledger"],
|
||||
["_Test Account Fixed Assets", "Current Assets", "Ledger"],
|
||||
]
|
||||
|
||||
test_objects = make_test_objects("Account", [[{
|
||||
"doctype": "Account",
|
||||
"account_name": account_name,
|
||||
"parent_account": parent_account,
|
||||
"company": "_Test Company",
|
||||
"group_or_ledger": group_or_ledger
|
||||
}] for account_name, parent_account, group_or_ledger in accounts])
|
||||
|
||||
webnotes.conn.set_value("Company", "_Test Company", "stock_in_hand_account",
|
||||
"_Test Account Stock In Hand - _TC")
|
||||
for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"]]:
|
||||
test_objects = make_test_objects("Account", [[{
|
||||
"doctype": "Account",
|
||||
"account_name": account_name,
|
||||
"parent_account": parent_account + " - " + abbr,
|
||||
"company": company,
|
||||
"group_or_ledger": group_or_ledger
|
||||
}] for account_name, parent_account, group_or_ledger in accounts])
|
||||
|
||||
return test_objects
|
||||
@@ -396,7 +396,7 @@ class DocType(BuyingController):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.get_company_default("expenses_included_in_valuation"),
|
||||
"cost_center": self.get_company_default("stock_adjustment_cost_center"),
|
||||
"cost_center": self.get_company_default("cost_center"),
|
||||
"against": self.doc.credit_to,
|
||||
"credit": valuation_tax,
|
||||
"remarks": self.doc.remarks or "Accounting Entry for Stock"
|
||||
|
||||
@@ -41,7 +41,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
for d in gl_entries:
|
||||
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
|
||||
|
||||
def atest_gl_entries_with_perpetual_accounting(self):
|
||||
def test_gl_entries_with_perpetual_accounting(self):
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 1)
|
||||
self.assertEqual(cint(webnotes.defaults.get_global_default("perpetual_accounting")), 1)
|
||||
|
||||
@@ -70,7 +70,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 0)
|
||||
|
||||
def atest_gl_entries_with_aia_for_non_stock_items(self):
|
||||
def test_gl_entries_with_aia_for_non_stock_items(self):
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 1)
|
||||
self.assertEqual(cint(webnotes.defaults.get_global_default("perpetual_accounting")), 1)
|
||||
|
||||
|
||||
@@ -557,10 +557,8 @@ class DocType(SellingController):
|
||||
if gl_entries:
|
||||
make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2),
|
||||
update_outstanding=update_outstanding, merge_entries=False)
|
||||
|
||||
warehouse_list = list(set([d.warehouse for d in
|
||||
self.doclist.get({"parentfield": "entries"})]))
|
||||
self.sync_stock_account_balance(warehouse_list)
|
||||
|
||||
self.update_gl_entries_after()
|
||||
|
||||
def make_customer_gl_entry(self, gl_entries):
|
||||
if self.doc.grand_total:
|
||||
@@ -605,13 +603,7 @@ class DocType(SellingController):
|
||||
# expense account gl entries
|
||||
if cint(webnotes.defaults.get_global_default("perpetual_accounting")) \
|
||||
and cint(self.doc.update_stock):
|
||||
for item in self.doclist.get({"parentfield": "entries"}):
|
||||
self.check_expense_account(item)
|
||||
|
||||
if item.buying_amount:
|
||||
|
||||
gl_entries += self.get_gl_entries_for_stock(item.expense_account,
|
||||
-1*item.buying_amount, item.warehouse, cost_center=item.cost_center)
|
||||
gl_entries += self.get_gl_entries_for_stock()
|
||||
|
||||
def make_pos_gl_entries(self, gl_entries):
|
||||
if cint(self.doc.is_pos) and self.doc.cash_bank_account and self.doc.paid_amount:
|
||||
|
||||
@@ -10,13 +10,25 @@ from accounts.utils import validate_expense_against_budget
|
||||
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True,
|
||||
update_outstanding='Yes'):
|
||||
if not cancel:
|
||||
if merge_entries:
|
||||
gl_map = merge_similar_entries(gl_map)
|
||||
|
||||
gl_map = process_gl_map(gl_map, merge_entries)
|
||||
save_entries(gl_map, adv_adj, update_outstanding)
|
||||
else:
|
||||
delete_gl_entries(gl_map, adv_adj, update_outstanding)
|
||||
|
||||
def process_gl_map(gl_map, merge_entries=True):
|
||||
if merge_entries:
|
||||
gl_map = merge_similar_entries(gl_map)
|
||||
|
||||
for entry in gl_map:
|
||||
# round off upto 2 decimal
|
||||
entry["debit"] = flt(entry["debit"], 2)
|
||||
entry["credit"] = flt(entry["credit"], 2)
|
||||
|
||||
# toggle debit, credit if negative entry
|
||||
if flt(entry["debit"]) < 0 or flt(entry["credit"]) < 0:
|
||||
entry["debit"], entry["credit"] = abs(flt(entry["credit"])), abs(flt(entry["debit"]))
|
||||
return gl_map
|
||||
|
||||
def merge_similar_entries(gl_map):
|
||||
merged_gl_map = []
|
||||
for entry in gl_map:
|
||||
@@ -31,7 +43,6 @@ def merge_similar_entries(gl_map):
|
||||
|
||||
# filter zero debit and credit entries
|
||||
merged_gl_map = filter(lambda x: flt(x["debit"])!=0 or flt(x["credit"])!=0, merged_gl_map)
|
||||
|
||||
return merged_gl_map
|
||||
|
||||
def check_if_in_list(gle, gl_mqp):
|
||||
@@ -43,31 +54,11 @@ def check_if_in_list(gle, gl_mqp):
|
||||
and cstr(e.get('cost_center')) == cstr(gle.get('cost_center')):
|
||||
return e
|
||||
|
||||
def check_budget(gl_map, cancel):
|
||||
for gle in gl_map:
|
||||
if gle.get('cost_center'):
|
||||
#check budget only if account is expense account
|
||||
acc_details = webnotes.conn.get_value("Account", gle['account'],
|
||||
['is_pl_account', 'debit_or_credit'])
|
||||
if acc_details[0]=="Yes" and acc_details[1]=="Debit":
|
||||
webnotes.get_obj('Budget Control').check_budget(gle, cancel)
|
||||
|
||||
def save_entries(gl_map, adv_adj, update_outstanding):
|
||||
total_debit = total_credit = 0.0
|
||||
def _swap(entry):
|
||||
entry["debit"], entry["credit"] = abs(flt(entry["credit"])), abs(flt(entry["debit"]))
|
||||
|
||||
for entry in gl_map:
|
||||
# round off upto 2 decimal
|
||||
entry["debit"] = flt(entry["debit"], 2)
|
||||
entry["credit"] = flt(entry["credit"], 2)
|
||||
|
||||
# toggle debit, credit if negative entry
|
||||
if flt(entry["debit"]) < 0 or flt(entry["credit"]) < 0:
|
||||
_swap(entry)
|
||||
|
||||
make_entry(entry, adv_adj, update_outstanding)
|
||||
|
||||
# check against budget
|
||||
validate_expense_against_budget(entry)
|
||||
|
||||
# update total debit / credit
|
||||
@@ -86,14 +77,14 @@ def make_entry(args, adv_adj, update_outstanding):
|
||||
|
||||
def validate_total_debit_credit(total_debit, total_credit):
|
||||
if abs(total_debit - total_credit) > 0.005:
|
||||
webnotes.throw(_("Debit and Credit not equal for this voucher: Diff (Debit) is ") +
|
||||
webnotes.throw(webnotes._("Debit and Credit not equal for this voucher: Diff (Debit) is ") +
|
||||
cstr(total_debit - total_credit))
|
||||
|
||||
def delete_gl_entries(gl_entries, adv_adj, update_outstanding):
|
||||
def delete_gl_entries(gl_entries=None, adv_adj=False, update_outstanding="Yes"):
|
||||
from accounts.doctype.gl_entry.gl_entry import check_negative_balance, \
|
||||
check_freezing_date, update_outstanding_amt, validate_freezed_account
|
||||
|
||||
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
||||
if gl_entries:
|
||||
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
||||
|
||||
webnotes.conn.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""",
|
||||
(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"]))
|
||||
|
||||
@@ -343,33 +343,24 @@ def validate_stock_and_account_balance():
|
||||
to enable perpetual accounting." +
|
||||
_(" Following accounts are not synced with stock balance") + ": \n" +
|
||||
"\n".join(difference.keys())), raise_exception=1)
|
||||
|
||||
def get_stock_and_account_difference(warehouse_list=None):
|
||||
from stock.utils import get_latest_stock_balance
|
||||
|
||||
if not warehouse_list:
|
||||
warehouse_list = webnotes.conn.sql_list("""select name from tabWarehouse
|
||||
where docstatus<2""")
|
||||
|
||||
def get_stock_and_account_difference(account_list=None, posting_date=None):
|
||||
from stock.utils import get_stock_balance_on
|
||||
|
||||
if not posting_date: posting_date = nowdate()
|
||||
|
||||
account_warehouse_map = {}
|
||||
warehouse_with_no_account = []
|
||||
difference = {}
|
||||
warehouse_account = webnotes.conn.sql("""select name, account from tabWarehouse
|
||||
where name in (%s)""" % ', '.join(['%s']*len(warehouse_list)), warehouse_list, as_dict=1)
|
||||
where account in (%s)""" % ', '.join(['%s']*len(account_list)), account_list, as_dict=1)
|
||||
|
||||
for wh in warehouse_account:
|
||||
if not wh.account: warehouse_with_no_account.append(wh.name)
|
||||
account_warehouse_map.setdefault(wh.account, []).append(wh.name)
|
||||
|
||||
if warehouse_with_no_account:
|
||||
msgprint(_("Please mention Perpetual Account in warehouse master for following warehouses")
|
||||
+ ": " + '\n'.join(warehouse_with_no_account), raise_exception=1)
|
||||
|
||||
bin_map = get_latest_stock_balance()
|
||||
for account, warehouse_list in account_warehouse_map.items():
|
||||
account_balance = get_balance_on(account)
|
||||
stock_value = sum([sum(bin_map.get(warehouse, {}).values())
|
||||
for warehouse in warehouse_list])
|
||||
account_balance = get_balance_on(account, posting_date)
|
||||
stock_value = get_stock_balance_on(warehouse_list, posting_date)
|
||||
|
||||
if abs(flt(stock_value) - flt(account_balance)) > 0.005:
|
||||
difference.setdefault(account, flt(stock_value) - flt(account_balance))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user