[fix] [minor] auto accounting for stock transactions

This commit is contained in:
Nabin Hait
2013-08-29 18:19:37 +05:30
parent d85d63bb81
commit bb77756069
18 changed files with 134 additions and 247 deletions

View File

@@ -12,19 +12,6 @@ class DocType:
def __init__(self, d, dl):
self.doc, self.doclist = d, dl
def validate(self):
self.validate_auto_accounting_for_stock()
def validate_auto_accounting_for_stock(self):
if cint(self.doc.auto_accounting_for_stock) == 1:
previous_val = cint(webnotes.conn.get_value("Accounts Settings",
None, "auto_accounting_for_stock"))
if cint(self.doc.auto_accounting_for_stock) != previous_val:
from accounts.utils import validate_stock_and_account_balance, \
create_stock_in_hand_jv
validate_stock_and_account_balance()
create_stock_in_hand_jv(reverse=cint(self.doc.auto_accounting_for_stock) < previous_val)
def on_update(self):
for key in ["auto_accounting_for_stock"]:
webnotes.conn.set_default(key, self.doc.fields.get(key, ''))

View File

@@ -32,6 +32,18 @@ class TestJournalVoucher(unittest.TestCase):
self.assertTrue(not webnotes.conn.sql("""select name from `tabJournal Voucher Detail`
where against_jv=%s""", jv_invoice.doc.name))
def test_jv_against_stock_account(self):
webnotes.defaults.set_global_default("auto_accounting_for_stock", 1)
jv = webnotes.bean(copy=test_records[0])
jv.doclist[1].account = "_Test Account Stock in Hand - _TC"
jv.insert()
from accounts.general_ledger import StockAccountInvalidTransaction
self.assertRaises(StockAccountInvalidTransaction, jv.submit)
webnotes.defaults.set_global_default("auto_accounting_for_stock", 0)
def test_monthly_budget_crossed_ignore(self):
webnotes.conn.set_value("Company", "_Test Company", "monthly_bgt_flag", "Ignore")

View File

@@ -90,7 +90,6 @@ class DocType(SellingController):
get_obj('Authorization Control').validate_approving_authority(self.doc.doctype,
self.doc.company, self.doc.grand_total, self)
self.set_buying_amount()
self.check_prev_docstatus()
self.update_status_updater_args()

View File

@@ -330,13 +330,12 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle)
def atest_pos_gl_entry_with_aii(self):
def test_pos_gl_entry_with_aii(self):
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
webnotes.conn.sql("delete from `tabGL Entry`")
webnotes.conn.sql("delete from `tabBin`")
webnotes.defaults.set_global_default("auto_accounting_for_stock", 1)
old_default_company = webnotes.conn.get_default("company")
webnotes.conn.set_default("company", "_Test Company")
self._insert_purchase_receipt()
self._insert_pos_settings()
@@ -360,20 +359,18 @@ class TestSalesInvoice(unittest.TestCase):
["_Test Item", "_Test Warehouse - _TC", -1.0])
# check gl entries
stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company",
"stock_in_hand_account")
gl_entries = webnotes.conn.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc, debit asc""", si.doc.name, as_dict=1)
self.assertTrue(gl_entries)
expected_gl_entries = sorted([
[si.doc.debit_to, 630.0, 0.0],
[pos[1]["income_account"], 0.0, 500.0],
[pos[2]["account_head"], 0.0, 80.0],
[pos[3]["account_head"], 0.0, 50.0],
[stock_in_hand_account, 0.0, 75.0],
["_Test Account Stock In Hand - _TC", 0.0, 75.0],
[pos[1]["expense_account"], 75.0, 0.0],
[si.doc.debit_to, 0.0, 600.0],
["_Test Account Bank Account - _TC", 600.0, 0.0]
@@ -383,6 +380,8 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(expected_gl_entries[i][1], gle.debit)
self.assertEquals(expected_gl_entries[i][2], gle.credit)
# cancel
si.cancel()
gle = webnotes.conn.sql("""select * from `tabGL Entry`
@@ -390,12 +389,11 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle)
self.assertFalse(get_stock_and_account_difference([si.doclist[1].warehouse]))
self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"]))
webnotes.defaults.set_global_default("auto_accounting_for_stock", 0)
webnotes.conn.set_default("company", old_default_company)
def atest_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)
si_copy = webnotes.copy_doclist(test_records[1])
@@ -422,7 +420,7 @@ class TestSalesInvoice(unittest.TestCase):
webnotes.defaults.set_global_default("auto_accounting_for_stock", 0)
def atest_sales_invoice_gl_entry_with_aii_non_stock_item(self):
def test_sales_invoice_gl_entry_with_aii_non_stock_item(self):
webnotes.defaults.set_global_default("auto_accounting_for_stock", 1)
si_copy = webnotes.copy_doclist(test_records[1])
@@ -641,7 +639,7 @@ class TestSalesInvoice(unittest.TestCase):
return new_si
# if yearly, test 3 repetitions, else test 13 repetitions
count = no_of_months == 12 and 3 or 13
count = 3 if no_of_months == 12 else 13
for i in xrange(count):
base_si = _test(i)

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-06-04 11:02:19",
"docstatus": 0,
"modified": "2013-07-25 16:32:10",
"modified": "2013-08-29 16:58:56",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -416,17 +416,6 @@
"print_hide": 1,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "buying_amount",
"fieldtype": "Currency",
"hidden": 1,
"label": "Buying Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1
},
{
"allow_on_submit": 1,
"doctype": "DocField",

View File

@@ -5,8 +5,12 @@ from __future__ import unicode_literals
import webnotes
from webnotes.utils import flt, cstr, now
from webnotes.model.doc import Document
from webnotes import msgprint, _
from accounts.utils import validate_expense_against_budget
class StockAccountInvalidTransaction(webnotes.ValidationError): pass
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True,
update_outstanding='Yes'):
if gl_map:
@@ -47,8 +51,8 @@ def merge_similar_entries(gl_map):
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):
for e in gl_mqp:
def check_if_in_list(gle, gl_map):
for e in gl_map:
if e.account == gle.account and \
cstr(e.get('against_voucher'))==cstr(gle.get('against_voucher')) \
and cstr(e.get('against_voucher_type')) == \
@@ -57,11 +61,14 @@ def check_if_in_list(gle, gl_mqp):
return e
def save_entries(gl_map, adv_adj, update_outstanding):
validate_account_for_auto_accounting_for_stock(gl_map)
total_debit = total_credit = 0.0
for entry in gl_map:
make_entry(entry, adv_adj, update_outstanding)
# check against budget
validate_expense_against_budget(entry)
# update total debit / credit
total_debit += flt(entry.debit)
@@ -79,8 +86,20 @@ 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(webnotes._("Debit and Credit not equal for this voucher: Diff (Debit) is ") +
webnotes.throw(_("Debit and Credit not equal for this voucher: Diff (Debit) is ") +
cstr(total_debit - total_credit))
def validate_account_for_auto_accounting_for_stock(gl_map):
if gl_map[0].voucher_type=="Journal Voucher":
aii_accounts = [d[0] for d in webnotes.conn.sql("""select account from tabWarehouse
where ifnull(account, '')!=''""")]
for entry in gl_map:
if entry.account in aii_accounts:
webnotes.throw(_("Account") + ": " + entry.account +
_(" can only be debited/credited through Stock transactions"),
StockAccountInvalidTransaction)
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
adv_adj=False, update_outstanding="Yes"):

View File

@@ -246,79 +246,6 @@ def get_company_default(company, fieldname):
_("' in Company: ") + company), raise_exception=True)
return value
def create_stock_in_hand_jv(reverse=False):
from webnotes.utils import nowdate
today = nowdate()
fiscal_year = get_fiscal_year(today)[0]
jv_list = []
for company in webnotes.conn.sql_list("select name from `tabCompany`"):
stock_rbnb_value = get_stock_rbnb_value(company)
stock_rbnb_value = reverse and -1*stock_rbnb_value or stock_rbnb_value
if stock_rbnb_value:
jv = webnotes.bean([
{
"doctype": "Journal Voucher",
"naming_series": "JV-AUTO-",
"company": company,
"posting_date": today,
"fiscal_year": fiscal_year,
"voucher_type": "Journal Entry",
"user_remark": (_("Perpetual Accounting") + ": " +
(_("Disabled") if reverse else _("Enabled")) + ". " +
_("Journal Entry for inventory that is received but not yet invoiced"))
},
{
"doctype": "Journal Voucher Detail",
"parentfield": "entries",
"account": get_company_default(company, "stock_received_but_not_billed"),
(stock_rbnb_value > 0 and "credit" or "debit"): abs(stock_rbnb_value)
},
{
"doctype": "Journal Voucher Detail",
"parentfield": "entries",
"account": get_company_default(company, "stock_adjustment_account"),
(stock_rbnb_value > 0 and "debit" or "credit"): abs(stock_rbnb_value),
"cost_center": get_company_default(company, "stock_adjustment_cost_center")
},
])
jv.insert()
jv_list.append(jv.doc.name)
if jv_list:
msgprint(_("Following Journal Vouchers have been created automatically") + \
":\n%s" % ("\n".join([("<a href=\"#Form/Journal Voucher/%s\">%s</a>" % (jv, jv)) for jv in jv_list]),))
msgprint(_("""These adjustment vouchers book the difference between \
the total value of received items and the total value of invoiced items, \
as a required step to use Perpetual Accounting.
This is an approximation to get you started.
You will need to submit these vouchers after checking if the values are correct.
For more details, read: \
<a href="http://erpnext.com/auto-inventory-accounting" target="_blank">\
Perpetual Accounting</a>"""))
webnotes.msgprint("""Please refresh the system to get effect of Perpetual Accounting""")
def get_stock_rbnb_value(company):
total_received_amount = webnotes.conn.sql("""select sum(valuation_rate*qty*conversion_factor)
from `tabPurchase Receipt Item` pr_item where docstatus=1
and exists(select name from `tabItem` where name = pr_item.item_code
and is_stock_item='Yes')
and exists(select name from `tabPurchase Receipt`
where name = pr_item.parent and company = %s)""", company)
total_billed_amount = webnotes.conn.sql("""select sum(valuation_rate*qty*conversion_factor)
from `tabPurchase Invoice Item` pi_item where docstatus=1
and exists(select name from `tabItem` where name = pi_item.item_code
and is_stock_item='Yes')
and exists(select name from `tabPurchase Invoice`
where name = pi_item.parent and company = %s)""", company)
return flt(total_received_amount[0][0]) - flt(total_billed_amount[0][0])
def fix_total_debit_credit():
vouchers = webnotes.conn.sql("""select voucher_type, voucher_no,
@@ -335,14 +262,6 @@ def fix_total_debit_credit():
where voucher_type = %s and voucher_no = %s and %s > 0 limit 1""" %
(dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr),
(d.diff, d.voucher_type, d.voucher_no))
def validate_stock_and_account_balance():
difference = get_stock_and_account_difference()
if difference:
msgprint(_("Account balance must be synced with stock 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(account_list=None, posting_date=None):
from stock.utils import get_stock_balance_on