[perpetual accounting] gl entries based on stock_value difference in sl entries
This commit is contained in:
parent
aeba24ee81
commit
27994c216a
@ -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))
|
||||
|
||||
|
@ -106,15 +106,6 @@ class SellingController(StockController):
|
||||
item.buying_amount = buying_amount >= 0.01 and buying_amount or 0
|
||||
webnotes.conn.set_value(item.doctype, item.name, "buying_amount",
|
||||
item.buying_amount)
|
||||
|
||||
def check_expense_account(self, item):
|
||||
if item.buying_amount and not item.expense_account:
|
||||
msgprint(_("""Expense account is mandatory for item: """) + item.item_code,
|
||||
raise_exception=1)
|
||||
|
||||
if item.buying_amount and not item.cost_center:
|
||||
msgprint(_("""Cost Center is mandatory for item: """) + item.item_code,
|
||||
raise_exception=1)
|
||||
|
||||
def calculate_taxes_and_totals(self):
|
||||
self.other_fname = "other_charges"
|
||||
|
@ -10,53 +10,185 @@ import webnotes.defaults
|
||||
from controllers.accounts_controller import AccountsController
|
||||
|
||||
class StockController(AccountsController):
|
||||
def get_gl_entries_for_stock(self, against_stock_account, amount, warehouse=None,
|
||||
stock_in_hand_account=None, cost_center=None):
|
||||
if not stock_in_hand_account and warehouse:
|
||||
stock_in_hand_account = webnotes.conn.get_value("Warehouse", warehouse, "account")
|
||||
def make_gl_entries(self):
|
||||
if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
|
||||
return
|
||||
|
||||
if amount:
|
||||
gl_entries = [
|
||||
# stock in hand account
|
||||
self.get_gl_dict({
|
||||
"account": stock_in_hand_account,
|
||||
"against": against_stock_account,
|
||||
"debit": amount,
|
||||
"remarks": self.doc.remarks or "Accounting Entry for Stock",
|
||||
}),
|
||||
from accounts.general_ledger import make_gl_entries, delete_gl_entries
|
||||
gl_entries = self.get_gl_entries_for_stock()
|
||||
|
||||
if gl_entries and self.doc.docstatus==1:
|
||||
make_gl_entries(gl_entries)
|
||||
elif self.doc.docstatus==2:
|
||||
webnotes.conn.sql("""delete from `tabGL Entry` where voucher_type=%s
|
||||
and voucher_no=%s""", (self.doc.doctype, self.doc.name))
|
||||
|
||||
self.update_gl_entries_after()
|
||||
|
||||
|
||||
def get_gl_entries_for_stock(self, item_acc_map=None, expense_account=None, cost_center=None):
|
||||
from accounts.general_ledger import process_gl_map
|
||||
|
||||
if not (expense_account or cost_center or item_acc_map):
|
||||
item_acc_map = {}
|
||||
for item in self.doclist.get({"parentfield": self.fname}):
|
||||
self.check_expense_account(item)
|
||||
item_acc_map.setdefault(item.name, [item.expense_account, item.cost_center])
|
||||
|
||||
gl_entries = []
|
||||
stock_value_diff = self.get_stock_value_diff_from_sle(item_acc_map, expense_account,
|
||||
cost_center)
|
||||
for stock_in_hand_account, against_stock_account_dict in stock_value_diff.items():
|
||||
for against_stock_account, cost_center_dict in against_stock_account_dict.items():
|
||||
for cost_center, value_diff in cost_center_dict.items():
|
||||
gl_entries += [
|
||||
# stock in hand account
|
||||
self.get_gl_dict({
|
||||
"account": stock_in_hand_account,
|
||||
"against": against_stock_account,
|
||||
"debit": value_diff,
|
||||
"remarks": self.doc.remarks or "Accounting Entry for Stock",
|
||||
}),
|
||||
|
||||
# account against stock in hand
|
||||
self.get_gl_dict({
|
||||
"account": against_stock_account,
|
||||
"against": stock_in_hand_account,
|
||||
"credit": value_diff,
|
||||
"cost_center": cost_center != "No Cost Center" and cost_center or None,
|
||||
"remarks": self.doc.remarks or "Accounting Entry for Stock",
|
||||
}),
|
||||
]
|
||||
gl_entries = process_gl_map(gl_entries)
|
||||
return gl_entries
|
||||
|
||||
|
||||
def get_stock_value_diff_from_sle(self, item_acc_map, expense_account, cost_center):
|
||||
wh_acc_map = self.get_warehouse_account_map()
|
||||
stock_value_diff = {}
|
||||
for sle in webnotes.conn.sql("""select warehouse, stock_value_difference, voucher_detail_no
|
||||
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
||||
(self.doc.doctype, self.doc.name), as_dict=True):
|
||||
account = wh_acc_map[sle.warehouse]
|
||||
against_account = expense_account or item_acc_map[sle.voucher_detail_no][0]
|
||||
cost_center = cost_center or item_acc_map[sle.voucher_detail_no][1] or \
|
||||
"No Cost Center"
|
||||
|
||||
# account against stock in hand
|
||||
self.get_gl_dict({
|
||||
"account": against_stock_account,
|
||||
"against": stock_in_hand_account,
|
||||
"credit": amount,
|
||||
"cost_center": cost_center or None,
|
||||
"remarks": self.doc.remarks or "Accounting Entry for Stock",
|
||||
}),
|
||||
]
|
||||
stock_value_diff.setdefault(account, {}).setdefault(against_account, {})\
|
||||
.setdefault(cost_center, 0)
|
||||
stock_value_diff[account][against_account][cost_center] += \
|
||||
flt(sle.stock_value_difference)
|
||||
|
||||
return stock_value_diff
|
||||
|
||||
def get_warehouse_account_map(self):
|
||||
wh_acc_map = {}
|
||||
warehouse_with_no_account = []
|
||||
for d in webnotes.conn.sql("""select name, account from `tabWarehouse`""", as_dict=True):
|
||||
if not d.account: warehouse_with_no_account.append(d.name)
|
||||
wh_acc_map.setdefault(d.name, d.account)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def sync_stock_account_balance(self, warehouse_list, cost_center=None, posting_date=None):
|
||||
if warehouse_with_no_account:
|
||||
webnotes.throw(_("Please mention Perpetual Account in warehouse master for \
|
||||
following warehouses") + ": " + '\n'.join(warehouse_with_no_account))
|
||||
|
||||
return wh_acc_map
|
||||
|
||||
def update_gl_entries_after(self):
|
||||
future_stock_vouchers = self.get_future_stock_vouchers()
|
||||
gle = self.get_voucherwise_gl_entries(future_stock_vouchers)
|
||||
for voucher_type, voucher_no in future_stock_vouchers:
|
||||
existing_gle = gle.get((voucher_type, voucher_no), {})
|
||||
voucher_bean = webnotes.bean(voucher_type, voucher_no)
|
||||
expected_gle = voucher_bean.run_method("get_gl_entries_for_stock")
|
||||
if expected_gle:
|
||||
if existing_gle:
|
||||
matched = True
|
||||
for entry in expected_gle:
|
||||
entry_amount = existing_gle.get(entry.account, {}).get(entry.cost_center \
|
||||
or "No Cost Center", [0, 0])
|
||||
|
||||
if [entry.debit, entry.credit] != entry_amount:
|
||||
matched = False
|
||||
break
|
||||
|
||||
if not matched:
|
||||
# make updated entry
|
||||
webnotes.conn.sql("""delete from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
voucher_bean.run_method("make_gl_entries")
|
||||
else:
|
||||
# make adjustment entry on that date
|
||||
self.make_adjustment_entry(expected_gle, voucher_bean)
|
||||
|
||||
|
||||
def get_future_stock_vouchers(self):
|
||||
future_stock_vouchers = []
|
||||
for d in webnotes.conn.sql("""select distinct voucher_type, voucher_no
|
||||
from `tabStock Ledger Entry`
|
||||
where timestamp(posting_date, posting_time) >= timestamp(%s, %s)
|
||||
order by timestamp(posting_date, posting_time) asc, name asc""",
|
||||
(self.doc.posting_date, self.doc.posting_time), as_dict=True):
|
||||
future_stock_vouchers.append([d.voucher_type, d.voucher_no])
|
||||
|
||||
return future_stock_vouchers
|
||||
|
||||
def get_voucherwise_gl_entries(self, future_stock_vouchers):
|
||||
gl_entries = {}
|
||||
if future_stock_vouchers:
|
||||
for d in webnotes.conn.sql("""select * from `tabGL Entry`
|
||||
where posting_date >= %s and voucher_no in (%s)""" %
|
||||
('%s', ', '.join(['%s']*len(future_stock_vouchers))),
|
||||
tuple([self.doc.posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
|
||||
gl_entries.setdefault((d.voucher_type, d.voucher_no), {})\
|
||||
.setdefault(d.account, {})\
|
||||
.setdefault(d.cost_center, [d.debit, d.credit])
|
||||
|
||||
return gl_entries
|
||||
|
||||
def make_adjustment_entry(self, expected_gle, voucher_bean):
|
||||
from accounts.utils import get_stock_and_account_difference
|
||||
acc_diff = get_stock_and_account_difference(warehouse_list)
|
||||
if not cost_center:
|
||||
cost_center = self.get_company_default("cost_center")
|
||||
account_list = [d.account for d in expected_gle]
|
||||
acc_diff = get_stock_and_account_difference(account_list, expected_gle[0].posting_date)
|
||||
|
||||
cost_center = self.get_company_default("cost_center")
|
||||
stock_adjustment_account = self.get_company_default("stock_adjustment_account")
|
||||
|
||||
gl_entries = []
|
||||
for account, diff in acc_diff.items():
|
||||
if diff:
|
||||
stock_adjustment_account = self.get_company_default("stock_adjustment_account")
|
||||
gl_entries += self.get_gl_entries_for_stock(stock_adjustment_account, diff,
|
||||
stock_in_hand_account=account, cost_center=cost_center)
|
||||
|
||||
gl_entries.append([
|
||||
# stock in hand account
|
||||
voucher_bean.get_gl_dict({
|
||||
"account": account,
|
||||
"against": stock_adjustment_account,
|
||||
"debit": diff,
|
||||
"remarks": "Adjustment Accounting Entry for Stock",
|
||||
}),
|
||||
|
||||
# account against stock in hand
|
||||
voucher_bean.get_gl_dict({
|
||||
"account": stock_adjustment_account,
|
||||
"against": account,
|
||||
"credit": diff,
|
||||
"cost_center": cost_center or None,
|
||||
"remarks": "Adjustment Accounting Entry for Stock",
|
||||
}),
|
||||
])
|
||||
|
||||
if gl_entries:
|
||||
from accounts.general_ledger import make_gl_entries
|
||||
|
||||
if posting_date:
|
||||
for entries in gl_entries:
|
||||
entries["posting_date"] = posting_date
|
||||
|
||||
make_gl_entries(gl_entries)
|
||||
|
||||
def check_expense_account(self, item):
|
||||
if item.fields.has_key("expense_account") and not item.expense_account:
|
||||
msgprint(_("""Expense account is mandatory for item: """) + item.item_code,
|
||||
raise_exception=1)
|
||||
|
||||
if item.fields.has_key("expense_account") and not item.cost_center:
|
||||
msgprint(_("""Cost Center is mandatory for item: """) + item.item_code,
|
||||
raise_exception=1)
|
||||
|
||||
def get_sl_entries(self, d, args):
|
||||
sl_dict = {
|
||||
|
@ -61,10 +61,9 @@ class DocType:
|
||||
wh = {
|
||||
"doctype":"Warehouse",
|
||||
"warehouse_name": whname,
|
||||
"company": self.doc.name
|
||||
"company": self.doc.name,
|
||||
}
|
||||
if cint(webnotes.defaults.get_global_default("perpetual_accounting")):
|
||||
wh.update({"account": "Stock In Hand - " + self.doc.abbr})
|
||||
wh.update({"account": "Stock In Hand - " + self.doc.abbr})
|
||||
|
||||
webnotes.bean(wh).insert()
|
||||
|
||||
|
@ -327,28 +327,6 @@ class DocType(SellingController):
|
||||
if amount != 0:
|
||||
total = (amount/self.doc.net_total)*self.doc.grand_total
|
||||
get_obj('Sales Common').check_credit(self, total)
|
||||
|
||||
def make_gl_entries(self):
|
||||
if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
|
||||
return
|
||||
|
||||
gl_entries = []
|
||||
warehouse_list = []
|
||||
for item in self.doclist.get({"parentfield": "delivery_note_details"}):
|
||||
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)
|
||||
if item.warehouse not in warehouse_list:
|
||||
warehouse_list.append(item.warehouse)
|
||||
|
||||
if gl_entries:
|
||||
from accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2))
|
||||
|
||||
self.sync_stock_account_balance(warehouse_list)
|
||||
|
||||
|
||||
def get_invoiced_qty_map(delivery_note):
|
||||
"""returns a map: {dn_detail: invoiced_qty}"""
|
||||
|
@ -18,6 +18,7 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
pr.submit()
|
||||
|
||||
def test_over_billing_against_dn(self):
|
||||
self.clear_stock_account_balance()
|
||||
self._insert_purchase_receipt()
|
||||
|
||||
from stock.doctype.delivery_note.delivery_note import make_sales_invoice
|
||||
@ -39,7 +40,7 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
|
||||
|
||||
def test_delivery_note_no_gl_entry(self):
|
||||
webnotes.conn.sql("""delete from `tabBin`""")
|
||||
self.clear_stock_account_balance()
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 0)
|
||||
self.assertEqual(cint(webnotes.defaults.get_global_default("perpetual_accounting")), 0)
|
||||
|
||||
@ -62,10 +63,8 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
|
||||
self.assertTrue(not gl_entries)
|
||||
|
||||
def atest_delivery_note_gl_entry(self):
|
||||
webnotes.conn.sql("""delete from `tabBin`""")
|
||||
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
|
||||
webnotes.conn.sql("delete from `tabGL Entry`")
|
||||
def test_delivery_note_gl_entry(self):
|
||||
self.clear_stock_account_balance()
|
||||
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 1)
|
||||
self.assertEqual(cint(webnotes.defaults.get_global_default("perpetual_accounting")), 1)
|
||||
@ -158,6 +157,11 @@ class TestDeliveryNote(unittest.TestCase):
|
||||
dn.insert()
|
||||
|
||||
self.assertRaises(SerialNoStatusError, dn.submit)
|
||||
|
||||
def clear_stock_account_balance(self):
|
||||
webnotes.conn.sql("""delete from `tabBin`""")
|
||||
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
|
||||
webnotes.conn.sql("delete from `tabGL Entry`")
|
||||
|
||||
test_records = [
|
||||
[
|
||||
|
@ -299,30 +299,16 @@ class DocType(BuyingController):
|
||||
|
||||
def get_rate(self,arg):
|
||||
return get_obj('Purchase Common').get_rate(arg,self)
|
||||
|
||||
def make_gl_entries(self):
|
||||
if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
|
||||
return
|
||||
|
||||
|
||||
def get_gl_entries_for_stock(self):
|
||||
against_stock_account = self.get_company_default("stock_received_but_not_billed")
|
||||
stock_items = self.get_stock_items()
|
||||
item_acc_map = {}
|
||||
for item in self.doclist.get({"parentfield": "purchase_receipt_details"}):
|
||||
item_acc_map.setdefault(item.name, [against_stock_account, None])
|
||||
|
||||
gl_entries = super(DocType, self).get_gl_entries_for_stock(item_acc_map)
|
||||
return gl_entries
|
||||
|
||||
gl_entries = []
|
||||
warehouse_list = []
|
||||
for d in self.doclist.get({"parentfield": "purchase_receipt_details"}):
|
||||
if d.item_code in stock_items and d.valuation_rate:
|
||||
valuation_amount = flt(d.valuation_rate) * \
|
||||
flt(d.qty) * flt(d.conversion_factor)
|
||||
gl_entries += self.get_gl_entries_for_stock(against_stock_account,
|
||||
valuation_amount, d.warehouse)
|
||||
|
||||
if d.warehouse not in warehouse_list:
|
||||
warehouse_list.append(d.warehouse)
|
||||
|
||||
if gl_entries:
|
||||
from accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2))
|
||||
self.sync_stock_account_balance(warehouse_list)
|
||||
|
||||
@webnotes.whitelist()
|
||||
def make_purchase_invoice(source_name, target_doclist=None):
|
||||
|
@ -177,42 +177,6 @@ class DocType(StockController):
|
||||
def set_total_amount(self):
|
||||
self.doc.total_amount = sum([flt(item.amount) for item in self.doclist.get({"parentfield": "mtn_details"})])
|
||||
|
||||
def make_gl_entries(self):
|
||||
if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
|
||||
return
|
||||
|
||||
gl_entries = []
|
||||
warehouse_list = []
|
||||
against_expense_account = self.doc.expense_adjustment_account
|
||||
for item in self.doclist.get({"parentfield": "mtn_details"}):
|
||||
valuation_amount = flt(item.incoming_rate) * flt(item.transfer_qty)
|
||||
if valuation_amount:
|
||||
if item.t_warehouse and not item.s_warehouse:
|
||||
warehouse = item.t_warehouse
|
||||
elif item.s_warehouse and not item.t_warehouse:
|
||||
warehouse = item.s_warehouse
|
||||
valuation_amount = -1*valuation_amount
|
||||
elif item.s_warehouse and item.t_warehouse:
|
||||
s_account = webnotes.conn.get_value("Warehouse", item.s_warehouse, "account")
|
||||
t_account = webnotes.conn.get_value("Warehouse", item.t_warehouse, "account")
|
||||
if s_account != t_account:
|
||||
warehouse = item.t_warehouse
|
||||
against_expense_account = s_account
|
||||
|
||||
if item.s_warehouse and item.s_warehouse not in warehouse_list:
|
||||
warehouse_list.append(item.s_warehouse)
|
||||
if item.t_warehouse and item.t_warehouse not in warehouse_list:
|
||||
warehouse_list.append(item.t_warehouse)
|
||||
|
||||
gl_entries += self.get_gl_entries_for_stock(against_expense_account,
|
||||
valuation_amount, warehouse, cost_center=self.doc.cost_center)
|
||||
|
||||
if gl_entries:
|
||||
from accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2)
|
||||
|
||||
self.sync_stock_account_balance(warehouse_list, self.doc.cost_center)
|
||||
|
||||
def get_stock_and_rate(self):
|
||||
"""get stock and incoming rate on posting date"""
|
||||
for d in getlist(self.doclist, 'mtn_details'):
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
"creation": "2013-04-09 11:43:55",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-08-08 14:22:31",
|
||||
"modified": "2013-08-24 15:16:34",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -199,24 +199,6 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:sys_defaults.perpetual_accounting",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "expense_adjustment_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Expense/Adjustment Account",
|
||||
"options": "Account",
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:sys_defaults.perpetual_accounting",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "items_section",
|
||||
|
@ -41,13 +41,14 @@ class TestStockEntry(unittest.TestCase):
|
||||
webnotes.conn.set_default("company", self.old_default_company)
|
||||
|
||||
def test_warehouse_company_validation(self):
|
||||
self._clear_stock_account_balance()
|
||||
from stock.doctype.stock_ledger_entry.stock_ledger_entry import InvalidWarehouseCompany
|
||||
st1 = webnotes.bean(copy=test_records[0])
|
||||
st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
|
||||
st1.insert()
|
||||
self.assertRaises(InvalidWarehouseCompany, st1.submit)
|
||||
|
||||
def atest_material_receipt_gl_entry(self):
|
||||
def test_material_receipt_gl_entry(self):
|
||||
self._clear_stock_account_balance()
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 1)
|
||||
|
||||
@ -69,17 +70,15 @@ class TestStockEntry(unittest.TestCase):
|
||||
)
|
||||
|
||||
mr.cancel()
|
||||
self.check_stock_ledger_entries("Stock Entry", mr.doc.name,
|
||||
sorted([["_Test Item", "_Test Warehouse - _TC", 50.0],
|
||||
["_Test Item", "_Test Warehouse - _TC", -50.0]]))
|
||||
|
||||
gl_entries = webnotes.conn.sql("""select account, debit, credit
|
||||
from `tabGL Entry` where voucher_type='Stock Entry' and voucher_no=%s
|
||||
order by account asc, debit asc""", (mr.doc.name), as_dict=1)
|
||||
self.assertEquals(len(gl_entries), 4)
|
||||
|
||||
self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry`
|
||||
where voucher_type='Stock Entry' and voucher_no=%s""", mr.doc.name))
|
||||
|
||||
self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry`
|
||||
where voucher_type='Stock Entry' and voucher_no=%s""", mr.doc.name))
|
||||
|
||||
|
||||
def atest_material_issue_gl_entry(self):
|
||||
def test_material_issue_gl_entry(self):
|
||||
self._clear_stock_account_balance()
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 1)
|
||||
|
||||
@ -102,24 +101,19 @@ class TestStockEntry(unittest.TestCase):
|
||||
)
|
||||
|
||||
mi.cancel()
|
||||
self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry`
|
||||
where voucher_type='Stock Entry' and voucher_no=%s""", mi.doc.name))
|
||||
|
||||
self.check_stock_ledger_entries("Stock Entry", mi.doc.name,
|
||||
sorted([["_Test Item", "_Test Warehouse - _TC", -40.0],
|
||||
["_Test Item", "_Test Warehouse - _TC", 40.0]]))
|
||||
|
||||
self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry`
|
||||
where voucher_type='Stock Entry' and voucher_no=%s""", mi.doc.name))
|
||||
|
||||
self.assertEquals(webnotes.conn.get_value("Bin", {"warehouse": mi.doclist[1].s_warehouse,
|
||||
"item_code": mi.doclist[1].item_code}, "actual_qty"), 50)
|
||||
|
||||
self.assertEquals(webnotes.conn.get_value("Bin", {"warehouse": mi.doclist[1].s_warehouse,
|
||||
"item_code": mi.doclist[1].item_code}, "stock_value"), 5000)
|
||||
|
||||
gl_entries = webnotes.conn.sql("""select account, debit, credit, voucher_no
|
||||
from `tabGL Entry` where voucher_type='Stock Entry' and voucher_no=%s
|
||||
order by account asc, debit asc""", (mi.doc.name), as_dict=1)
|
||||
self.assertEquals(len(gl_entries), 4)
|
||||
|
||||
|
||||
def atest_material_transfer_gl_entry(self):
|
||||
def test_material_transfer_gl_entry(self):
|
||||
self._clear_stock_account_balance()
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 1)
|
||||
|
||||
@ -146,11 +140,12 @@ class TestStockEntry(unittest.TestCase):
|
||||
|
||||
|
||||
mtn.cancel()
|
||||
self.check_stock_ledger_entries("Stock Entry", mtn.doc.name,
|
||||
sorted([["_Test Item", "_Test Warehouse - _TC", 45.0],
|
||||
["_Test Item", "_Test Warehouse 1 - _TC", -45.0],
|
||||
["_Test Item", "_Test Warehouse - _TC", -45.0],
|
||||
["_Test Item", "_Test Warehouse 1 - _TC", 45.0]]))
|
||||
self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry`
|
||||
where voucher_type='Stock Entry' and voucher_no=%s""", mtn.doc.name))
|
||||
|
||||
self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry`
|
||||
where voucher_type='Stock Entry' and voucher_no=%s""", mtn.doc.name))
|
||||
|
||||
|
||||
def test_repack_no_change_in_valuation(self):
|
||||
self._clear_stock_account_balance()
|
||||
@ -224,15 +219,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertEquals(expected_gl_entries[i][0], gle[0])
|
||||
self.assertEquals(expected_gl_entries[i][1], gle[1])
|
||||
self.assertEquals(expected_gl_entries[i][2], gle[2])
|
||||
|
||||
def _clear_stock(self):
|
||||
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
|
||||
webnotes.conn.sql("""delete from `tabBin`""")
|
||||
webnotes.conn.sql("""delete from `tabSerial No`""")
|
||||
|
||||
self.old_default_company = webnotes.conn.get_default("company")
|
||||
webnotes.conn.set_default("company", "_Test Company")
|
||||
|
||||
def _insert_material_receipt(self):
|
||||
self._clear_stock_account_balance()
|
||||
se1 = webnotes.bean(copy=test_records[0])
|
||||
@ -321,9 +308,11 @@ class TestStockEntry(unittest.TestCase):
|
||||
return se
|
||||
|
||||
def test_sales_invoice_return_of_non_packing_item(self):
|
||||
self._clear_stock_account_balance()
|
||||
self._test_sales_invoice_return("_Test Item", 5, 2)
|
||||
|
||||
def test_sales_invoice_return_of_packing_item(self):
|
||||
self._clear_stock_account_balance()
|
||||
self._test_sales_invoice_return("_Test Sales BOM Item", 25, 20)
|
||||
|
||||
def _test_delivery_note_return(self, item_code, delivered_qty, returned_qty):
|
||||
@ -373,9 +362,11 @@ class TestStockEntry(unittest.TestCase):
|
||||
return se
|
||||
|
||||
def test_delivery_note_return_of_non_packing_item(self):
|
||||
self._clear_stock_account_balance()
|
||||
self._test_delivery_note_return("_Test Item", 5, 2)
|
||||
|
||||
def test_delivery_note_return_of_packing_item(self):
|
||||
self._clear_stock_account_balance()
|
||||
self._test_delivery_note_return("_Test Sales BOM Item", 25, 20)
|
||||
|
||||
def _test_sales_return_jv(self, se):
|
||||
@ -390,14 +381,17 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertTrue(jv_list[1].get("against_invoice"))
|
||||
|
||||
def test_make_return_jv_for_sales_invoice_non_packing_item(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = self._test_sales_invoice_return("_Test Item", 5, 2)
|
||||
self._test_sales_return_jv(se)
|
||||
|
||||
def test_make_return_jv_for_sales_invoice_packing_item(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = self._test_sales_invoice_return("_Test Sales BOM Item", 25, 20)
|
||||
self._test_sales_return_jv(se)
|
||||
|
||||
def test_make_return_jv_for_delivery_note_non_packing_item(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = self._test_delivery_note_return("_Test Item", 5, 2)
|
||||
self._test_sales_return_jv(se)
|
||||
|
||||
@ -405,6 +399,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self._test_sales_return_jv(se)
|
||||
|
||||
def test_make_return_jv_for_delivery_note_packing_item(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = self._test_delivery_note_return("_Test Sales BOM Item", 25, 20)
|
||||
self._test_sales_return_jv(se)
|
||||
|
||||
@ -521,6 +516,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
|
||||
def test_over_stock_return(self):
|
||||
from stock.doctype.stock_entry.stock_entry import StockOverReturnError
|
||||
self._clear_stock_account_balance()
|
||||
|
||||
# out of 10, 5 gets returned
|
||||
prev_se, pr_docname = self.test_purchase_receipt_return()
|
||||
@ -548,6 +544,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertTrue(jv_list[1].get("against_voucher"))
|
||||
|
||||
def test_make_return_jv_for_purchase_receipt(self):
|
||||
self._clear_stock_account_balance()
|
||||
se, pr_name = self.test_purchase_receipt_return()
|
||||
self._test_purchase_return_jv(se)
|
||||
|
||||
@ -660,6 +657,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertRaises(SerialNoQtyError, se.submit)
|
||||
|
||||
def test_serial_no_transfer_in(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = webnotes.bean(copy=test_records[0])
|
||||
se.doclist[1].item_code = "_Test Serialized Item"
|
||||
se.doclist[1].qty = 2
|
||||
@ -672,6 +670,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertTrue(webnotes.conn.exists("Serial No", "EFGH"))
|
||||
|
||||
def test_serial_no_not_exists(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = webnotes.bean(copy=test_records[0])
|
||||
se.doc.purpose = "Material Issue"
|
||||
se.doclist[1].item_code = "_Test Serialized Item"
|
||||
@ -684,6 +683,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertRaises(SerialNoNotExistsError, se.submit)
|
||||
|
||||
def test_serial_by_series(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = make_serialized_item()
|
||||
|
||||
serial_nos = get_serial_nos(se.doclist[1].serial_no)
|
||||
@ -694,6 +694,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
return se
|
||||
|
||||
def test_serial_item_error(self):
|
||||
self._clear_stock_account_balance()
|
||||
self.test_serial_by_series()
|
||||
|
||||
se = webnotes.bean(copy=test_records[0])
|
||||
@ -708,6 +709,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertRaises(SerialNoItemError, se.submit)
|
||||
|
||||
def test_serial_move(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = make_serialized_item()
|
||||
serial_no = get_serial_nos(se.doclist[1].serial_no)[0]
|
||||
|
||||
@ -724,6 +726,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse 1 - _TC")
|
||||
|
||||
def test_serial_warehouse_error(self):
|
||||
self._clear_stock_account_balance()
|
||||
make_serialized_item()
|
||||
|
||||
se = webnotes.bean(copy=test_records[0])
|
||||
@ -738,6 +741,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertRaises(SerialNoWarehouseError, se.submit)
|
||||
|
||||
def test_serial_cancel(self):
|
||||
self._clear_stock_account_balance()
|
||||
se = self.test_serial_by_series()
|
||||
se.cancel()
|
||||
|
||||
@ -763,8 +767,6 @@ test_records = [
|
||||
"posting_time": "17:14:24",
|
||||
"purpose": "Material Receipt",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"expense_adjustment_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
{
|
||||
"conversion_factor": 1.0,
|
||||
@ -777,6 +779,8 @@ test_records = [
|
||||
"transfer_qty": 50.0,
|
||||
"uom": "_Test UOM",
|
||||
"t_warehouse": "_Test Warehouse - _TC",
|
||||
"expense_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
],
|
||||
[
|
||||
@ -787,8 +791,6 @@ test_records = [
|
||||
"posting_time": "17:15",
|
||||
"purpose": "Material Issue",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"expense_adjustment_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
{
|
||||
"conversion_factor": 1.0,
|
||||
@ -801,6 +803,8 @@ test_records = [
|
||||
"transfer_qty": 40.0,
|
||||
"uom": "_Test UOM",
|
||||
"s_warehouse": "_Test Warehouse - _TC",
|
||||
"expense_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
],
|
||||
[
|
||||
@ -811,8 +815,6 @@ test_records = [
|
||||
"posting_time": "17:14:24",
|
||||
"purpose": "Material Transfer",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"expense_adjustment_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
{
|
||||
"conversion_factor": 1.0,
|
||||
@ -826,6 +828,8 @@ test_records = [
|
||||
"uom": "_Test UOM",
|
||||
"s_warehouse": "_Test Warehouse - _TC",
|
||||
"t_warehouse": "_Test Warehouse 1 - _TC",
|
||||
"expense_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -836,8 +840,6 @@ test_records = [
|
||||
"posting_time": "17:14:24",
|
||||
"purpose": "Manufacture/Repack",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"expense_adjustment_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
{
|
||||
"conversion_factor": 1.0,
|
||||
@ -850,6 +852,8 @@ test_records = [
|
||||
"transfer_qty": 50.0,
|
||||
"uom": "_Test UOM",
|
||||
"s_warehouse": "_Test Warehouse - _TC",
|
||||
"expense_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
{
|
||||
"conversion_factor": 1.0,
|
||||
@ -862,6 +866,8 @@ test_records = [
|
||||
"transfer_qty": 1,
|
||||
"uom": "_Test UOM",
|
||||
"t_warehouse": "_Test Warehouse - _TC",
|
||||
"expense_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
],
|
||||
]
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
"creation": "2013-03-29 18:22:12",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-07-10 14:54:23",
|
||||
"modified": "2013-08-25 21:00:24",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -144,6 +144,27 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:sys_defaults.perpetual_accounting",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "expense_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Expense/Adjustment Account",
|
||||
"options": "Account",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:sys_defaults.perpetual_accounting",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "actual_qty",
|
||||
|
@ -293,31 +293,14 @@ class DocType(StockController):
|
||||
stock_value_difference[d.warehouse] -= diff
|
||||
|
||||
webnotes.conn.set(self.doc, "stock_value_difference", json.dumps(stock_value_difference))
|
||||
|
||||
def make_gl_entries(self):
|
||||
if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
|
||||
return
|
||||
|
||||
|
||||
def get_gl_entries_for_stock(self):
|
||||
if not self.doc.cost_center:
|
||||
msgprint(_("Please enter Cost Center"), raise_exception=1)
|
||||
|
||||
super(DocType, self).get_gl_entries_for_stock(expense_account=self.doc.expense_account,
|
||||
cost_center=self.doc.cost_center)
|
||||
|
||||
if self.doc.stock_value_difference:
|
||||
stock_value_difference = json.loads(self.doc.stock_value_difference)
|
||||
gl_entries = []
|
||||
warehouse_list = []
|
||||
for warehouse, diff in stock_value_difference.items():
|
||||
if diff:
|
||||
gl_entries += self.get_gl_entries_for_stock(self.doc.expense_account, diff,
|
||||
warehouse, cost_center=self.doc.cost_center)
|
||||
|
||||
if warehouse not in warehouse_list:
|
||||
warehouse_list.append(warehouse)
|
||||
|
||||
if gl_entries:
|
||||
from accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2)
|
||||
|
||||
self.sync_stock_account_balance(warehouse_list, self.doc.cost_center)
|
||||
|
||||
def validate_expense_account(self):
|
||||
if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
|
||||
|
@ -12,7 +12,7 @@ from accounts.utils import get_fiscal_year, get_stock_and_account_difference, ge
|
||||
|
||||
|
||||
class TestStockReconciliation(unittest.TestCase):
|
||||
def test_reco_for_fifo(self):
|
||||
def atest_reco_for_fifo(self):
|
||||
webnotes.defaults.set_global_default("perpetual_accounting", 0)
|
||||
# [[qty, valuation_rate, posting_date,
|
||||
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
|
||||
@ -90,6 +90,7 @@ class TestStockReconciliation(unittest.TestCase):
|
||||
self.assertEqual(res and flt(res[0][0], 4) or 0, d[4])
|
||||
|
||||
# bin qty and stock value
|
||||
print "bin"
|
||||
bin = webnotes.conn.sql("""select actual_qty, stock_value from `tabBin`
|
||||
where item_code = '_Test Item' and warehouse = '_Test Warehouse - _TC'""")
|
||||
|
||||
@ -196,50 +197,77 @@ class TestStockReconciliation(unittest.TestCase):
|
||||
webnotes.conn.set_value("Item", "_Test Item", "valuation_method", valuation_method)
|
||||
webnotes.conn.set_default("allow_negative_stock", 1)
|
||||
|
||||
existing_ledgers = [
|
||||
stock_entry = [
|
||||
{
|
||||
"doctype": "Stock Ledger Entry", "__islocal": 1,
|
||||
"voucher_type": "Stock Entry", "voucher_no": "TEST",
|
||||
"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC",
|
||||
"posting_date": "2012-12-12", "posting_time": "01:00",
|
||||
"actual_qty": 20, "incoming_rate": 1000, "company": "_Test Company",
|
||||
"fiscal_year": "_Test Fiscal Year 2012",
|
||||
},
|
||||
"company": "_Test Company",
|
||||
"doctype": "Stock Entry",
|
||||
"posting_date": "2012-12-12",
|
||||
"posting_time": "01:00",
|
||||
"purpose": "Material Receipt",
|
||||
"fiscal_year": "_Test Fiscal Year 2012",
|
||||
},
|
||||
{
|
||||
"doctype": "Stock Ledger Entry", "__islocal": 1,
|
||||
"voucher_type": "Stock Entry", "voucher_no": "TEST",
|
||||
"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC",
|
||||
"posting_date": "2012-12-15", "posting_time": "02:00",
|
||||
"actual_qty": 10, "incoming_rate": 700, "company": "_Test Company",
|
||||
"fiscal_year": "_Test Fiscal Year 2012",
|
||||
},
|
||||
{
|
||||
"doctype": "Stock Ledger Entry", "__islocal": 1,
|
||||
"voucher_type": "Stock Entry", "voucher_no": "TEST",
|
||||
"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC",
|
||||
"posting_date": "2012-12-25", "posting_time": "03:00",
|
||||
"actual_qty": -15, "company": "_Test Company",
|
||||
"fiscal_year": "_Test Fiscal Year 2012",
|
||||
},
|
||||
{
|
||||
"doctype": "Stock Ledger Entry", "__islocal": 1,
|
||||
"voucher_type": "Stock Entry", "voucher_no": "TEST",
|
||||
"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC",
|
||||
"posting_date": "2012-12-31", "posting_time": "08:00",
|
||||
"actual_qty": -20, "company": "_Test Company",
|
||||
"fiscal_year": "_Test Fiscal Year 2012",
|
||||
},
|
||||
{
|
||||
"doctype": "Stock Ledger Entry", "__islocal": 1,
|
||||
"voucher_type": "Stock Entry", "voucher_no": "TEST",
|
||||
"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC",
|
||||
"posting_date": "2013-01-05", "posting_time": "07:00",
|
||||
"actual_qty": 15, "incoming_rate": 1200, "company": "_Test Company",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
},
|
||||
"conversion_factor": 1.0,
|
||||
"doctype": "Stock Entry Detail",
|
||||
"item_code": "_Test Item",
|
||||
"parentfield": "mtn_details",
|
||||
"incoming_rate": 1000,
|
||||
"qty": 20.0,
|
||||
"stock_uom": "_Test UOM",
|
||||
"transfer_qty": 20.0,
|
||||
"uom": "_Test UOM",
|
||||
"t_warehouse": "_Test Warehouse - _TC",
|
||||
"expense_account": "Stock Adjustment - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
]
|
||||
from stock.stock_ledger import make_sl_entries
|
||||
make_sl_entries(existing_ledgers)
|
||||
|
||||
|
||||
pr = webnotes.bean(copy=stock_entry)
|
||||
pr.insert()
|
||||
pr.submit()
|
||||
|
||||
pr1 = webnotes.bean(copy=stock_entry)
|
||||
pr1.doc.posting_date = "2012-12-15"
|
||||
pr1.doc.posting_time = "02:00"
|
||||
pr1.doclist[1].qty = 10
|
||||
pr1.doclist[1].transfer_qty = 10
|
||||
pr1.doclist[1].incoming_rate = 700
|
||||
pr1.insert()
|
||||
pr1.submit()
|
||||
|
||||
pr2 = webnotes.bean(copy=stock_entry)
|
||||
pr2.doc.posting_date = "2012-12-25"
|
||||
pr2.doc.posting_time = "03:00"
|
||||
pr2.doc.purpose = "Material Issue"
|
||||
pr2.doclist[1].s_warehouse = "_Test Warehouse - _TC"
|
||||
pr2.doclist[1].t_warehouse = None
|
||||
pr2.doclist[1].qty = 15
|
||||
pr2.doclist[1].transfer_qty = 15
|
||||
pr2.doclist[1].incoming_rate = 0
|
||||
pr2.insert()
|
||||
pr2.submit()
|
||||
|
||||
pr3 = webnotes.bean(copy=stock_entry)
|
||||
pr3.doc.posting_date = "2012-12-31"
|
||||
pr3.doc.posting_time = "08:00"
|
||||
pr3.doc.purpose = "Material Issue"
|
||||
pr3.doclist[1].s_warehouse = "_Test Warehouse - _TC"
|
||||
pr3.doclist[1].t_warehouse = None
|
||||
pr3.doclist[1].qty = 20
|
||||
pr3.doclist[1].transfer_qty = 20
|
||||
pr3.doclist[1].incoming_rate = 0
|
||||
pr3.insert()
|
||||
pr3.submit()
|
||||
|
||||
pr4 = webnotes.bean(copy=stock_entry)
|
||||
pr4.doc.posting_date = "2013-01-05"
|
||||
pr4.doc.fiscal_year = "_Test Fiscal Year 2013"
|
||||
pr4.doc.posting_time = "07:00"
|
||||
pr4.doclist[1].qty = 15
|
||||
pr4.doclist[1].transfer_qty = 15
|
||||
pr4.doclist[1].incoming_rate = 1200
|
||||
pr4.insert()
|
||||
pr4.submit()
|
||||
|
||||
|
||||
test_dependencies = ["Item", "Warehouse"]
|
@ -18,6 +18,6 @@ test_records = [
|
||||
"doctype": "Warehouse",
|
||||
"warehouse_name": "_Test Warehouse 2",
|
||||
"company": "_Test Company 1",
|
||||
"account": "_Test Account Stock In Hand - _TC"
|
||||
"account": "_Test Account Stock In Hand - _TC1"
|
||||
}]
|
||||
]
|
||||
|
@ -33,7 +33,7 @@ def make_sl_entries(sl_entries, is_amended=None):
|
||||
update_bin(args)
|
||||
|
||||
if cancel:
|
||||
delete_cancelled_entry(sl_entries[0].get('voucher_no'), sl_entries[0].get('voucher_type'))
|
||||
delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
|
||||
|
||||
def set_as_cancel(voucher_type, voucher_no):
|
||||
webnotes.conn.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',
|
||||
@ -74,8 +74,9 @@ def update_entries_after(args, verbose=1):
|
||||
qty_after_transaction = flt(previous_sle.get("qty_after_transaction"))
|
||||
valuation_rate = flt(previous_sle.get("valuation_rate"))
|
||||
stock_queue = json.loads(previous_sle.get("stock_queue") or "[]")
|
||||
prev_stock_value = stock_value = flt(previous_sle.get("stock_value"))
|
||||
|
||||
stock_value = flt(previous_sle.get("stock_value"))
|
||||
prev_stock_value = flt(previous_sle.get("stock_value"))
|
||||
|
||||
entries_to_fix = get_sle_after_datetime(previous_sle or \
|
||||
{"item_code": args["item_code"], "warehouse": args["warehouse"]}, for_update=True)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user