Merge branch 'master' into perpetual
diff --git a/accounts/doctype/account/test_account.py b/accounts/doctype/account/test_account.py
index 502f0e5..10b3f92 100644
--- a/accounts/doctype/account/test_account.py
+++ b/accounts/doctype/account/test_account.py
@@ -15,6 +15,7 @@
["_Test Account Shipping Charges", "_Test Account Stock Expenses - _TC", "Ledger"],
["_Test Account Customs Duty", "_Test Account Stock Expenses - _TC", "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"],
@@ -28,6 +29,7 @@
# related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets - _TC", "Ledger"],
+ ["_Test Account Fixed Assets", "Current Assets - _TC", "Ledger"],
]
test_objects = make_test_objects("Account", [[{
diff --git a/accounts/doctype/accounts_settings/accounts_settings.py b/accounts/doctype/accounts_settings/accounts_settings.py
index 96f324a..d1a4c25 100644
--- a/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/accounts/doctype/accounts_settings/accounts_settings.py
@@ -5,23 +5,24 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cint
+from webnotes.utils import cint, cstr
+from webnotes import msgprint, _
class DocType:
def __init__(self, d, dl):
self.doc, self.doclist = d, dl
def validate(self):
- self.make_adjustment_jv_for_auto_inventory()
+ self.validate_perpetual_accounting()
- def make_adjustment_jv_for_auto_inventory(self):
- previous_auto_inventory_accounting = cint(webnotes.conn.get_value("Accounts Settings",
- None, "auto_inventory_accounting"))
- if cint(self.doc.auto_inventory_accounting) != previous_auto_inventory_accounting:
- from accounts.utils import create_stock_in_hand_jv
- create_stock_in_hand_jv(reverse = \
- cint(self.doc.auto_inventory_accounting) < previous_auto_inventory_accounting)
-
+ def validate_perpetual_accounting(self):
+ if cint(self.doc.perpetual_accounting) == 1:
+ previous_val = cint(webnotes.conn.get_value("Accounts Settings",
+ None, "perpetual_accounting"))
+ if cint(self.doc.perpetual_accounting) != previous_val:
+ from accounts.utils import validate_stock_and_account_balance
+ validate_stock_and_account_balance()
+
def on_update(self):
- for key in ["auto_inventory_accounting"]:
+ for key in ["perpetual_accounting"]:
webnotes.conn.set_default(key, self.doc.fields.get(key, ''))
diff --git a/accounts/doctype/accounts_settings/accounts_settings.txt b/accounts/doctype/accounts_settings/accounts_settings.txt
index b8be161..b7ab69e 100644
--- a/accounts/doctype/accounts_settings/accounts_settings.txt
+++ b/accounts/doctype/accounts_settings/accounts_settings.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-06-24 15:49:57",
"docstatus": 0,
- "modified": "2013-07-05 14:23:40",
+ "modified": "2013-08-01 17:35:16",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -39,11 +39,12 @@
"name": "Accounts Settings"
},
{
+ "default": "1",
"description": "If enabled, the system will post accounting entries for inventory automatically.",
"doctype": "DocField",
- "fieldname": "auto_inventory_accounting",
+ "fieldname": "perpetual_accounting",
"fieldtype": "Check",
- "label": "Enable Auto Inventory Accounting"
+ "label": "Enable Perpetual Accounting for Inventory"
},
{
"description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",
diff --git a/accounts/doctype/gl_entry/gl_entry.py b/accounts/doctype/gl_entry/gl_entry.py
index 9c1cf3f..1aad21f 100644
--- a/accounts/doctype/gl_entry/gl_entry.py
+++ b/accounts/doctype/gl_entry/gl_entry.py
@@ -38,7 +38,7 @@
for k in mandatory:
if not self.doc.fields.get(k):
msgprint(k + _(" is mandatory for GL Entry"), raise_exception=1)
-
+
# Zero value transaction is not allowed
if not (flt(self.doc.debit) or flt(self.doc.credit)):
msgprint(_("GL Entry: Debit or Credit amount is mandatory for ") + self.doc.account,
@@ -56,6 +56,7 @@
def validate_posting_date(self):
from accounts.utils import validate_fiscal_year
validate_fiscal_year(self.doc.posting_date, self.doc.fiscal_year, "Posting Date")
+
def check_credit_limit(self):
master_type, master_name = webnotes.conn.get_value("Account",
diff --git a/accounts/doctype/pos_setting/pos_setting.py b/accounts/doctype/pos_setting/pos_setting.py
index 73f5ed6..a3984a6 100755
--- a/accounts/doctype/pos_setting/pos_setting.py
+++ b/accounts/doctype/pos_setting/pos_setting.py
@@ -20,7 +20,7 @@
def validate(self):
self.check_for_duplicate()
self.validate_expense_account()
-
+
def check_for_duplicate(self):
res = webnotes.conn.sql("""select name, user from `tabPOS Setting`
where ifnull(user, '') = %s and name != %s and company = %s""",
@@ -34,6 +34,6 @@
(res[0][0], self.doc.company), raise_exception=1)
def validate_expense_account(self):
- if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \
+ if cint(webnotes.defaults.get_global_default("perpetual_accounting")) \
and not self.doc.expense_account:
msgprint(_("Expense Account is mandatory"), raise_exception=1)
\ No newline at end of file
diff --git a/accounts/doctype/pos_setting/pos_setting.txt b/accounts/doctype/pos_setting/pos_setting.txt
index 9f5b246..b04f704 100755
--- a/accounts/doctype/pos_setting/pos_setting.txt
+++ b/accounts/doctype/pos_setting/pos_setting.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-24 12:15:51",
"docstatus": 0,
- "modified": "2013-08-01 16:50:05",
+ "modified": "2013-08-05 16:51:22",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -163,7 +163,7 @@
"reqd": 1
},
{
- "depends_on": "eval:sys_defaults.auto_inventory_accounting",
+ "depends_on": "eval:sys_defaults.perpetual_accounting",
"doctype": "DocField",
"fieldname": "expense_account",
"fieldtype": "Link",
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py
index 516c014..e9043c0 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -212,28 +212,29 @@
raise Exception
def set_against_expense_account(self):
- auto_inventory_accounting = \
- cint(webnotes.defaults.get_global_default("auto_inventory_accounting"))
+ perpetual_accounting = cint(webnotes.defaults.get_global_default("perpetual_accounting"))
- if auto_inventory_accounting:
+ if perpetual_accounting:
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
against_accounts = []
+ stock_items = self.get_stock_items()
for item in self.doclist.get({"parentfield": "entries"}):
- if auto_inventory_accounting and item.item_code in self.stock_items:
+ if perpetual_accounting and item.item_code in stock_items:
# in case of auto inventory accounting, against expense account is always
# Stock Received But Not Billed for a stock item
- item.expense_head = item.cost_center = None
+ item.expense_head = stock_not_billed_account
+ item.cost_center = None
if stock_not_billed_account not in against_accounts:
against_accounts.append(stock_not_billed_account)
elif not item.expense_head:
- msgprint(_("""Expense account is mandatory for item: """) + (item.item_code or item.item_name),
- raise_exception=1)
+ msgprint(_("Expense account is mandatory for item") + ": " +
+ (item.item_code or item.item_name), raise_exception=1)
elif item.expense_head not in against_accounts:
- # if no auto_inventory_accounting or not a stock item
+ # if no perpetual_accounting or not a stock item
against_accounts.append(item.expense_head)
self.doc.against_expense_account = ",".join(against_accounts)
@@ -316,9 +317,8 @@
self.update_prevdoc_status()
def make_gl_entries(self):
- from accounts.general_ledger import make_gl_entries
- auto_inventory_accounting = \
- cint(webnotes.defaults.get_global_default("auto_inventory_accounting"))
+ perpetual_accounting = \
+ cint(webnotes.defaults.get_global_default("perpetual_accounting"))
gl_entries = []
@@ -355,17 +355,15 @@
valuation_tax += (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount)
# item gl entries
- stock_item_and_auto_inventory_accounting = False
- if auto_inventory_accounting:
- stock_account = self.get_company_default("stock_received_but_not_billed")
-
+ stock_item_and_perpetual_accounting = False
+ stock_items = self.get_stock_items()
for item in self.doclist.get({"parentfield": "entries"}):
- if auto_inventory_accounting and item.item_code in self.stock_items:
+ if perpetual_accounting and item.item_code in stock_items:
if flt(item.valuation_rate):
# if auto inventory accounting enabled and stock item,
# then do stock related gl entries
# expense will be booked in sales invoice
- stock_item_and_auto_inventory_accounting = True
+ stock_item_and_perpetual_accounting = True
valuation_amt = (flt(item.amount, self.precision("amount", item)) +
flt(item.item_tax_amount, self.precision("item_tax_amount", item)) +
@@ -373,7 +371,7 @@
gl_entries.append(
self.get_gl_dict({
- "account": stock_account,
+ "account": item.expense_head,
"against": self.doc.credit_to,
"debit": valuation_amt,
"remarks": self.doc.remarks or "Accounting Entry for Stock"
@@ -392,7 +390,7 @@
})
)
- if stock_item_and_auto_inventory_accounting and valuation_tax:
+ if stock_item_and_perpetual_accounting and valuation_tax:
# credit valuation tax amount in "Expenses Included In Valuation"
# this will balance out valuation amount included in cost of goods sold
gl_entries.append(
@@ -419,6 +417,7 @@
)
if gl_entries:
+ from accounts.general_ledger import make_gl_entries
make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2))
def on_cancel(self):
@@ -458,4 +457,4 @@
and tabAccount.company = '%(company)s'
and tabAccount.%(key)s LIKE '%(txt)s'
%(mcond)s""" % {'company': filters['company'], 'key': searchfield,
- 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype, searchfield)})
\ No newline at end of file
+ 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype, searchfield)})
diff --git a/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 7d68602..c9b5e05 100644
--- a/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -14,9 +14,9 @@
test_ignore = ["Serial No"]
class TestPurchaseInvoice(unittest.TestCase):
- def test_gl_entries_without_auto_inventory_accounting(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
- self.assertTrue(not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")))
+ def test_gl_entries_without_perpetual_accounting(self):
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
+ self.assertTrue(not cint(webnotes.defaults.get_global_default("perpetual_accounting")))
wrapper = webnotes.bean(copy=test_records[0])
wrapper.run_method("calculate_taxes_and_totals")
@@ -41,9 +41,9 @@
for d in gl_entries:
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
- def test_gl_entries_with_auto_inventory_accounting(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
- self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 1)
+ 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)
pi = webnotes.bean(copy=test_records[1])
pi.run_method("calculate_taxes_and_totals")
@@ -68,11 +68,11 @@
self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
def test_gl_entries_with_aia_for_non_stock_items(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
- self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 1)
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
+ self.assertEqual(cint(webnotes.defaults.get_global_default("perpetual_accounting")), 1)
pi = webnotes.bean(copy=test_records[1])
pi.doclist[1].item_code = "_Test Non Stock Item"
@@ -99,7 +99,7 @@
self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
def test_purchase_invoice_calculation(self):
wrapper = webnotes.bean(copy=test_records[0])
diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js
index bc36f58..ef3110a 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/accounts/doctype/sales_invoice/sales_invoice.js
@@ -318,7 +318,7 @@
});
// expense account
-if (sys_defaults.auto_inventory_accounting) {
+if (sys_defaults.perpetual_accounting) {
cur_frm.fields_dict['entries'].grid.get_field('expense_account').get_query = function(doc) {
return {
filters: {
diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py
index 5a2ad4d..c282e0f 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/accounts/doctype/sales_invoice/sales_invoice.py
@@ -92,7 +92,7 @@
sl_obj.update_serial_record(self, 'entries', is_submit = 1, is_incoming = 0)
sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
- self.update_stock_ledger(update_stock=1)
+ self.update_stock_ledger()
else:
# Check for Approving Authority
if not self.doc.recurring_id:
@@ -124,7 +124,7 @@
sl.update_serial_record(self, 'entries', is_submit = 0, is_incoming = 0)
sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
- self.update_stock_ledger(update_stock = -1)
+ self.update_stock_ledger()
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self)
@@ -536,45 +536,20 @@
submitted = webnotes.conn.sql("select name from `tabDelivery Note` where docstatus = 1 and name = '%s'" % d.delivery_note)
if not submitted:
msgprint("Delivery Note : "+ cstr(d.delivery_note) +" is not submitted")
- raise Exception , "Validation Error."
+ raise Exception , "Validation Error."
-
- def make_sl_entry(self, d, wh, qty, in_value, update_stock):
- st_uom = webnotes.conn.sql("select stock_uom from `tabItem` where name = '%s'"%d['item_code'])
- self.values.append({
- 'item_code' : d['item_code'],
- 'warehouse' : wh,
- 'posting_date' : self.doc.posting_date,
- 'posting_time' : self.doc.posting_time,
- 'voucher_type' : 'Sales Invoice',
- 'voucher_no' : cstr(self.doc.name),
- 'voucher_detail_no' : cstr(d['name']),
- 'actual_qty' : qty,
- 'stock_uom' : st_uom and st_uom[0][0] or '',
- 'incoming_rate' : in_value,
- 'company' : self.doc.company,
- 'fiscal_year' : self.doc.fiscal_year,
- 'is_cancelled' : (update_stock==1) and 'No' or 'Yes',
- 'batch_no' : cstr(d['batch_no']),
- 'serial_no' : d['serial_no'],
- "project" : self.doc.project_name
- })
-
- def update_stock_ledger(self, update_stock):
- self.values = []
+ def update_stock_ledger(self):
+ sl_entries = []
items = get_obj('Sales Common').get_item_list(self)
for d in items:
- stock_item = webnotes.conn.sql("SELECT is_stock_item, is_sample_item \
- FROM tabItem where name = '%s'"%(d['item_code']), as_dict = 1)
- if stock_item[0]['is_stock_item'] == "Yes":
- if not d['warehouse']:
- msgprint("Message: Please enter Warehouse for item %s as it is stock item." \
- % d['item_code'], raise_exception=1)
-
- # Reduce actual qty from warehouse
- self.make_sl_entry( d, d['warehouse'], - flt(d['qty']) , 0, update_stock)
-
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values)
+ if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
+ and d.warehouse:
+ sl_entries.append(self.get_sl_entries(d, {
+ "actual_qty": -1*flt(d.qty),
+ "stock_uom": webnotes.conn.get_value("Item", d.item_code, "stock_uom")
+ }))
+
+ self.make_sl_entries(sl_entries)
def make_gl_entries(self):
from accounts.general_ledger import make_gl_entries, merge_similar_entries
@@ -598,6 +573,10 @@
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)
+
def make_customer_gl_entry(self, gl_entries):
if self.doc.grand_total:
gl_entries.append(
@@ -639,7 +618,7 @@
)
# expense account gl entries
- if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \
+ if cint(webnotes.defaults.get_global_default("perpetual_accounting")) \
and cint(self.doc.update_stock):
for item in self.doclist.get({"parentfield": "entries"}):
@@ -647,7 +626,7 @@
if item.buying_amount:
gl_entries += self.get_gl_entries_for_stock(item.expense_account,
- -1*item.buying_amount, cost_center=item.cost_center)
+ -1*item.buying_amount, item.warehouse, cost_center=item.cost_center)
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:
diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py
index 95bbf67..47ff6e4 100644
--- a/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -297,7 +297,7 @@
"Batched for Billing")
def test_sales_invoice_gl_entry_without_aii(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
si = webnotes.bean(copy=test_records[1])
si.insert()
@@ -330,9 +330,9 @@
self.assertEquals(gle_count[0][0], 8)
- def test_pos_gl_entry_with_aii(self):
+ def atest_pos_gl_entry_with_aii(self):
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
old_default_company = webnotes.conn.get_default("company")
webnotes.conn.set_default("company", "_Test Company")
@@ -392,11 +392,11 @@
self.assertEquals(gl_count[0][0], 16)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
webnotes.conn.set_default("company", old_default_company)
def test_sales_invoice_gl_entry_with_aii_no_item_code(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
si_copy = webnotes.copy_doclist(test_records[1])
si_copy[1]["item_code"] = None
@@ -420,10 +420,10 @@
self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
def test_sales_invoice_gl_entry_with_aii_non_stock_item(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
si_copy = webnotes.copy_doclist(test_records[1])
si_copy[1]["item_code"] = "_Test Non Stock Item"
@@ -447,7 +447,7 @@
self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
def _insert_purchase_receipt(self):
from stock.doctype.purchase_receipt.test_purchase_receipt import test_records \
diff --git a/accounts/utils.py b/accounts/utils.py
index 9beaac7..29bf638 100644
--- a/accounts/utils.py
+++ b/accounts/utils.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import nowdate, cstr, flt, now
+from webnotes.utils import nowdate, nowtime, cstr, flt, now
from webnotes.model.doc import addchild
from webnotes import msgprint, _
from webnotes.utils import formatdate
@@ -338,4 +338,44 @@
webnotes.conn.sql("""update `tabGL Entry` set %s = %s + %s
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))
\ No newline at end of file
+ (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(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""")
+
+ 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)
+
+ 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])
+
+ if flt(stock_value) - flt(account_balance):
+ difference.setdefault(account, flt(stock_value) - flt(account_balance))
+
+ return difference
diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py
index 42e9995..a536d92 100644
--- a/controllers/accounts_controller.py
+++ b/controllers/accounts_controller.py
@@ -21,7 +21,7 @@
self.set_total_in_words()
self.validate_for_freezed_account()
-
+
def set_missing_values(self, for_validate=False):
for fieldname in ["posting_date", "transaction_date"]:
if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
@@ -398,20 +398,17 @@
def get_company_default(self, fieldname):
from accounts.utils import get_company_default
return get_company_default(self.doc.company, fieldname)
-
- @property
- def stock_items(self):
- if not hasattr(self, "_stock_items"):
- self._stock_items = []
- item_codes = list(set(item.item_code for item in
- self.doclist.get({"parentfield": self.fname})))
- if item_codes:
- self._stock_items = [r[0] for r in webnotes.conn.sql("""select name
- from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \
- (", ".join((["%s"]*len(item_codes))),), item_codes)]
+ def get_stock_items(self):
+ stock_items = []
+ item_codes = list(set(item.item_code for item in
+ self.doclist.get({"parentfield": self.fname})))
+ if item_codes:
+ stock_items = [r[0] for r in webnotes.conn.sql("""select name
+ from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \
+ (", ".join((["%s"]*len(item_codes))),), item_codes)]
- return self._stock_items
+ return stock_items
@property
def company_abbr(self):
diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py
index 4006b37..c5f5d08 100644
--- a/controllers/buying_controller.py
+++ b/controllers/buying_controller.py
@@ -52,7 +52,7 @@
raise_exception=WrongWarehouseCompany)
def validate_stock_or_nonstock_items(self):
- if not self.stock_items:
+ if not self.get_stock_items():
tax_for_valuation = [d.account_head for d in
self.doclist.get({"parentfield": "purchase_tax_details"})
if d.category in ["Valuation", "Valuation and Total"]]
diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py
index eb2e9ef..9aa93a8 100644
--- a/controllers/selling_controller.py
+++ b/controllers/selling_controller.py
@@ -100,13 +100,13 @@
item_sales_bom.setdefault(d.parent_item, []).append(new_d)
if stock_ledger_entries:
+ stock_items = self.get_stock_items()
for item in self.doclist.get({"parentfield": self.fname}):
- if item.item_code in self.stock_items or \
+ if item.item_code in stock_items or \
(item_sales_bom and item_sales_bom.get(item.item_code)):
buying_amount = get_buying_amount(item.item_code, item.warehouse, -1*item.qty,
self.doc.doctype, self.doc.name, item.name, stock_ledger_entries,
item_sales_bom)
-
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)
diff --git a/controllers/stock_controller.py b/controllers/stock_controller.py
index 8384973..a66e190 100644
--- a/controllers/stock_controller.py
+++ b/controllers/stock_controller.py
@@ -3,17 +3,17 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cint
+from webnotes.utils import cint, flt, cstr
+from webnotes import msgprint, _
import webnotes.defaults
+
from controllers.accounts_controller import AccountsController
class StockController(AccountsController):
- def get_gl_entries_for_stock(self, against_stock_account, amount,
+ 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:
- stock_in_hand_account = self.get_company_default("stock_in_hand_account")
- if not cost_center:
- cost_center = self.get_company_default("stock_adjustment_cost_center")
+ if not stock_in_hand_account and warehouse:
+ stock_in_hand_account = webnotes.conn.get_value("Warehouse", warehouse, "account")
if amount:
gl_entries = [
@@ -36,6 +36,57 @@
]
return gl_entries
+
+ def sync_stock_account_balance(self, warehouse_list, cost_center=None, posting_date=None):
+ print "sync_stock_account_balance"
+ 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")
+ print acc_diff
+ 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)
+
+ if gl_entries:
+ from accounts.general_ledger import make_gl_entries
+
+ if posting_date:
+ for entries in gl_entries:
+ entries["posting_date"] = posting_date
+ # print gl_entries
+ make_gl_entries(gl_entries)
+
+ def get_sl_entries(self, d, args):
+ sl_dict = {
+ "item_code": d.item_code,
+ "warehouse": d.warehouse,
+ "posting_date": self.doc.posting_date,
+ "posting_time": self.doc.posting_time,
+ "voucher_type": self.doc.doctype,
+ "voucher_no": self.doc.name,
+ "voucher_detail_no": d.name,
+ "actual_qty": (self.doc.docstatus==1 and 1 or -1)*flt(d.stock_qty),
+ "stock_uom": d.stock_uom,
+ "incoming_rate": 0,
+ "company": self.doc.company,
+ "fiscal_year": self.doc.fiscal_year,
+ "is_cancelled": self.doc.docstatus==2 and "Yes" or "No",
+ "batch_no": cstr(d.batch_no).strip(),
+ "serial_no": d.serial_no,
+ "project": d.project_name
+ }
+
+ sl_dict.update(args)
+ return sl_dict
+
+ def make_sl_entries(self, sl_entries, is_amended=None):
+ if sl_entries:
+ from webnotes.model.code import get_obj
+ get_obj('Stock Ledger').update_stock(sl_entries, is_amended)
def get_stock_ledger_entries(self, item_list=None, warehouse_list=None):
if not (item_list and warehouse_list):
diff --git a/patches/august_2013/p01_perpetual_accounting_patch.py b/patches/august_2013/p01_perpetual_accounting_patch.py
new file mode 100644
index 0000000..de231a1
--- /dev/null
+++ b/patches/august_2013/p01_perpetual_accounting_patch.py
@@ -0,0 +1,33 @@
+import webnotes
+from webnotes.utils import cint
+
+def execute():
+ copy_perpetual_accounting_settings()
+ set_missing_cost_center()
+
+def set_missing_cost_center():
+ reload_docs = [
+ ["stock", "doctype", "serial_no"],
+ ["stock", "doctype", "stock_reconciliation"],
+ ["stock", "doctype", "stock_entry"]
+ ]
+ for d in reload_docs:
+ webnotes.reload_doc(d[0], d[1], d[2])
+
+ if cint(webnotes.defaults.get_global_default("perpetual_accounting")):
+ for dt in ["Serial No", "Stock Reconciliation", "Stock Entry"]:
+ webnotes.conn.sql("""update `tab%s` t1, tabCompany t2
+ set t1.cost_center=t2.stock_adjustment_cost_center
+ where t1.company = t2.name""" % dt)
+
+def copy_perpetual_accounting_settings():
+ webnotes.reload_doc("accounts", "doctype", "accounts_settings")
+ aii_enabled = cint(webnotes.conn.get_value("Global Defaults", None,
+ "auto_inventory_accounting"))
+ if aii_enabled:
+ try:
+ bean= webnotes.bean("Account Settings")
+ bean.doc.perpetual_accounting = aii_enabled
+ bean.save()
+ except:
+ pass
\ No newline at end of file
diff --git a/patches/june_2013/p09_update_global_defaults.py b/patches/june_2013/p09_update_global_defaults.py
index 0f8131a..0fe9247 100644
--- a/patches/june_2013/p09_update_global_defaults.py
+++ b/patches/june_2013/p09_update_global_defaults.py
@@ -6,7 +6,6 @@
def execute():
from_global_defaults = {
"credit_controller": "Accounts Settings",
- "auto_inventory_accounting": "Accounts Settings",
"acc_frozen_upto": "Accounts Settings",
"bde_auth_role": "Accounts Settings",
"auto_indent": "Stock Settings",
diff --git a/patches/may_2013/p01_conversion_factor_and_aii.py b/patches/may_2013/p01_conversion_factor_and_aii.py
index 17af01b..3821827 100644
--- a/patches/may_2013/p01_conversion_factor_and_aii.py
+++ b/patches/may_2013/p01_conversion_factor_and_aii.py
@@ -8,8 +8,7 @@
def execute():
webnotes.conn.auto_commit_on_many_writes = True
- aii_enabled = cint(webnotes.conn.get_value("Global Defaults", None,
- "auto_inventory_accounting"))
+ aii_enabled = cint(webnotes.defaults.get_global_default("perpetual_accounting"))
if aii_enabled:
create_stock_in_hand_jv(reverse = True)
diff --git a/patches/may_2013/p05_update_cancelled_gl_entries.py b/patches/may_2013/p05_update_cancelled_gl_entries.py
index ece5bb2..1c9cc84 100644
--- a/patches/may_2013/p05_update_cancelled_gl_entries.py
+++ b/patches/may_2013/p05_update_cancelled_gl_entries.py
@@ -6,8 +6,7 @@
from webnotes.utils import cint
def execute():
- aii_enabled = cint(webnotes.conn.get_value("Global Defaults", None,
- "auto_inventory_accounting"))
+ aii_enabled = cint(webnotes.defaults.get_global_default("perpetual_accounting"))
if aii_enabled:
webnotes.conn.sql("""update `tabGL Entry` gle set is_cancelled = 'Yes'
diff --git a/patches/patch_list.py b/patches/patch_list.py
index f2e94d0..235d914 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -251,5 +251,6 @@
"patches.july_2013.p10_change_partner_user_to_website_user",
"patches.july_2013.p11_update_price_list_currency",
"execute:webnotes.bean('Selling Settings').save() #2013-07-29",
+ "patches.august_2013.p01_perpetual_accounting_patch",
"patches.august_2013.p01_hr_settings",
]
\ No newline at end of file
diff --git a/public/js/controllers/stock_controller.js b/public/js/controllers/stock_controller.js
index a5b5107..f90317e 100644
--- a/public/js/controllers/stock_controller.js
+++ b/public/js/controllers/stock_controller.js
@@ -20,7 +20,7 @@
},
show_general_ledger: function() {
var me = this;
- if(this.frm.doc.docstatus===1 && cint(wn.defaults.get_default("auto_inventory_accounting"))) {
+ if(this.frm.doc.docstatus===1 && cint(wn.defaults.get_default("perpetual_accounting"))) {
cur_frm.add_custom_button('Accounting Ledger', function() {
wn.route_options = {
"voucher_no": me.frm.doc.name,
diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py
index 04ac891..9d89fce 100644
--- a/selling/doctype/sales_common/sales_common.py
+++ b/selling/doctype/sales_common/sales_common.py
@@ -140,7 +140,7 @@
for p in getlist(obj.doclist, 'packing_details'):
if p.parent_detail_docname == d.name and p.parent_item == d.item_code:
# the packing details table's qty is already multiplied with parent's qty
- il.append({
+ il.append(webnotes._dict({
'warehouse': p.warehouse,
'reserved_warehouse': reserved_warehouse,
'item_code': p.item_code,
@@ -150,9 +150,9 @@
'batch_no': cstr(p.batch_no).strip(),
'serial_no': cstr(p.serial_no).strip(),
'name': d.name
- })
+ }))
else:
- il.append({
+ il.append(webnotes._dict({
'warehouse': d.warehouse,
'reserved_warehouse': reserved_warehouse,
'item_code': d.item_code,
@@ -162,7 +162,7 @@
'batch_no': cstr(d.batch_no).strip(),
'serial_no': cstr(d.serial_no).strip(),
'name': d.name
- })
+ }))
return il
def get_already_delivered_qty(self, dn, so, so_detail):
diff --git a/selling/doctype/sms_center/sms_center.py b/selling/doctype/sms_center/sms_center.py
index c3b5ac0..29e793c 100644
--- a/selling/doctype/sms_center/sms_center.py
+++ b/selling/doctype/sms_center/sms_center.py
@@ -42,7 +42,7 @@
for d in rec:
rec_list += d[0] + ' - ' + d[1] + '\n'
self.doc.receiver_list = rec_list
- webnotes.errprint(rec_list)
+
def get_receiver_nos(self):
receiver_nos = []
for d in self.doc.receiver_list.split('\n'):
diff --git a/setup/doctype/company/company.js b/setup/doctype/company/company.js
index 6ae1626..40e314c 100644
--- a/setup/doctype/company/company.js
+++ b/setup/doctype/company/company.js
@@ -59,18 +59,7 @@
}
}
-if (sys_defaults.auto_inventory_accounting) {
- cur_frm.fields_dict["stock_in_hand_account"].get_query = function(doc) {
- return {
- "filters": {
- "is_pl_account": "No",
- "debit_or_credit": "Debit",
- "company": doc.name,
- 'group_or_ledger': "Ledger"
- }
- }
- }
-
+if (sys_defaults.perpetual_accounting) {
cur_frm.fields_dict["stock_adjustment_account"].get_query = function(doc) {
return {
"filters": {
@@ -95,10 +84,4 @@
}
}
}
-
- cur_frm.fields_dict["stock_adjustment_cost_center"].get_query = function(doc) {
- return {
- "filters": {"company": doc.name}
- }
- }
}
\ No newline at end of file
diff --git a/setup/doctype/company/company.txt b/setup/doctype/company/company.txt
index 768c7b1..e147843 100644
--- a/setup/doctype/company/company.txt
+++ b/setup/doctype/company/company.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-04-10 08:35:39",
"docstatus": 0,
- "modified": "2013-08-05 15:39:36",
+ "modified": "2013-08-05 17:23:52",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -25,13 +25,20 @@
"permlevel": 0
},
{
+ "amend": 0,
+ "cancel": 1,
+ "create": 1,
"doctype": "DocPerm",
"name": "__common__",
"parent": "Company",
"parentfield": "permissions",
"parenttype": "DocType",
"permlevel": 0,
- "read": 1
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "submit": 0,
+ "write": 1
},
{
"doctype": "DocType",
@@ -221,19 +228,9 @@
{
"depends_on": "eval:!doc.__islocal",
"doctype": "DocField",
- "fieldname": "auto_inventory_accounting_settings",
+ "fieldname": "perpetual_accounting_settings",
"fieldtype": "Section Break",
- "label": "Auto Inventory Accounting Settings",
- "read_only": 0
- },
- {
- "description": "This account will be used to maintain value of available stock",
- "doctype": "DocField",
- "fieldname": "stock_in_hand_account",
- "fieldtype": "Link",
- "label": "Stock In Hand Account",
- "no_copy": 1,
- "options": "Account",
+ "label": "Perpetual Accounting Settings",
"read_only": 0
},
{
@@ -247,13 +244,6 @@
},
{
"doctype": "DocField",
- "fieldname": "col_break23",
- "fieldtype": "Column Break",
- "read_only": 0,
- "width": "50%"
- },
- {
- "doctype": "DocField",
"fieldname": "stock_adjustment_account",
"fieldtype": "Link",
"label": "Stock Adjustment Account",
@@ -271,15 +261,6 @@
"read_only": 0
},
{
- "doctype": "DocField",
- "fieldname": "stock_adjustment_cost_center",
- "fieldtype": "Link",
- "label": "Stock Adjustment Cost Center",
- "no_copy": 1,
- "options": "Cost Center",
- "read_only": 0
- },
- {
"description": "For reference only.",
"doctype": "DocField",
"fieldname": "company_info",
@@ -374,17 +355,6 @@
"read_only": 1
},
{
- "amend": 0,
- "cancel": 1,
- "create": 1,
- "doctype": "DocPerm",
- "report": 1,
- "role": "System Manager",
- "submit": 0,
- "write": 1
- },
- {
- "doctype": "DocPerm",
- "role": "All"
+ "doctype": "DocPerm"
}
]
\ No newline at end of file
diff --git a/setup/doctype/setup_control/setup_control.py b/setup/doctype/setup_control/setup_control.py
index 54c0e28..2dd1646 100644
--- a/setup/doctype/setup_control/setup_control.py
+++ b/setup/doctype/setup_control/setup_control.py
@@ -106,8 +106,8 @@
})
global_defaults.save()
- webnotes.conn.set_value("Accounts Settings", None, "auto_inventory_accounting", 1)
- webnotes.conn.set_default("auto_inventory_accounting", 1)
+ webnotes.conn.set_value("Accounts Settings", None, "perpetual_accounting", 1)
+ webnotes.conn.set_default("perpetual_accounting", 1)
stock_settings = webnotes.bean("Stock Settings")
stock_settings.doc.item_naming_by = "Item Code"
diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js
index 6d601c2..eaaf9ea 100644
--- a/stock/doctype/delivery_note/delivery_note.js
+++ b/stock/doctype/delivery_note/delivery_note.js
@@ -33,8 +33,8 @@
set_print_hide(doc, dt, dn);
- // unhide expense_account and cost_center is auto_inventory_accounting enabled
- var aii_enabled = cint(sys_defaults.auto_inventory_accounting)
+ // unhide expense_account and cost_center is perpetual_accounting enabled
+ var aii_enabled = cint(sys_defaults.perpetual_accounting)
cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp(["expense_account", "cost_center"], aii_enabled);
if (this.frm.doc.docstatus===0) {
@@ -187,7 +187,7 @@
}
}
-if (sys_defaults.auto_inventory_accounting) {
+if (sys_defaults.perpetual_accounting) {
cur_frm.cscript.expense_account = function(doc, cdt, cdn){
var d = locals[cdt][cdn];
diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py
index 679d743..b94cc05 100644
--- a/stock/doctype/delivery_note/delivery_note.py
+++ b/stock/doctype/delivery_note/delivery_note.py
@@ -161,7 +161,7 @@
if not d['warehouse']:
msgprint("Please enter Warehouse for item %s as it is stock item"
% d['item_code'], raise_exception=1)
-
+
def update_current_stock(self):
for d in getlist(self.doclist, 'delivery_note_details'):
@@ -200,7 +200,7 @@
self.update_prevdoc_status()
# create stock ledger entry
- self.update_stock_ledger(update_stock = 1)
+ self.update_stock_ledger()
self.credit_limit()
@@ -245,7 +245,7 @@
self.update_prevdoc_status()
- self.update_stock_ledger(update_stock = -1)
+ self.update_stock_ledger()
webnotes.conn.set(self.doc, 'status', 'Cancelled')
self.cancel_packing_slips()
@@ -279,14 +279,11 @@
webnotes.msgprint(_("Packing Slip(s) Cancelled"))
- def update_stock_ledger(self, update_stock):
- self.values = []
+ def update_stock_ledger(self):
+ sl_entries = []
for d in self.get_item_list():
- if webnotes.conn.get_value("Item", d['item_code'], "is_stock_item") == "Yes":
- # this happens when item is changed from non-stock to stock item
- if not d["warehouse"]:
- continue
-
+ if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
+ and d.warehouse:
if d['reserved_qty'] < 0 :
# Reduce reserved qty from reserved warehouse mentioned in so
if not d["reserved_warehouse"]:
@@ -296,43 +293,21 @@
"item_code": d['item_code'],
"voucher_type": self.doc.doctype,
"voucher_no": self.doc.name,
- "reserved_qty": flt(update_stock) * flt(d['reserved_qty']),
+ "reserved_qty": (self.doc.docstatus==1 and 1 or -1)*flt(d['reserved_qty']),
"posting_date": self.doc.posting_date,
"is_amended": self.doc.amended_from and 'Yes' or 'No'
}
get_obj("Warehouse", d["reserved_warehouse"]).update_bin(args)
# Reduce actual qty from warehouse
- self.make_sl_entry(d, d['warehouse'], - flt(d['qty']) , 0, update_stock)
-
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values)
-
+ sl_entries.append(self.get_sl_entries(d, {
+ "actual_qty": -1*flt(d['qty']),
+ }))
+ self.make_sl_entries(sl_entries)
def get_item_list(self):
return get_obj('Sales Common').get_item_list(self)
-
- def make_sl_entry(self, d, wh, qty, in_value, update_stock):
- self.values.append({
- 'item_code' : d['item_code'],
- 'warehouse' : wh,
- 'posting_date' : self.doc.posting_date,
- 'posting_time' : self.doc.posting_time,
- 'voucher_type' : 'Delivery Note',
- 'voucher_no' : self.doc.name,
- 'voucher_detail_no' : d['name'],
- 'actual_qty' : qty,
- 'stock_uom' : d['uom'],
- 'incoming_rate' : in_value,
- 'company' : self.doc.company,
- 'fiscal_year' : self.doc.fiscal_year,
- 'is_cancelled' : (update_stock==1) and 'No' or 'Yes',
- 'batch_no' : d['batch_no'],
- 'serial_no' : d['serial_no'],
- "project" : self.doc.project_name
- })
-
-
def credit_limit(self):
"""check credit limit of items in DN Detail which are not fetched from sales order"""
amount, total = 0, 0
@@ -344,20 +319,26 @@
get_obj('Sales Common').check_credit(self, total)
def make_gl_entries(self):
- if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")):
+ 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,
- cost_center=item.cost_center)
+ 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}"""
diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py
index 641a564..3f7c753 100644
--- a/stock/doctype/delivery_note/test_delivery_note.py
+++ b/stock/doctype/delivery_note/test_delivery_note.py
@@ -37,8 +37,8 @@
def test_delivery_note_no_gl_entry(self):
webnotes.conn.sql("""delete from `tabBin`""")
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
- self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
+ self.assertEqual(cint(webnotes.defaults.get_global_default("perpetual_accounting")), 0)
self._insert_purchase_receipt()
@@ -56,8 +56,8 @@
webnotes.conn.sql("""delete from `tabBin`""")
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
- self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 1)
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
+ self.assertEqual(cint(webnotes.defaults.get_global_default("perpetual_accounting")), 1)
self._insert_purchase_receipt()
@@ -93,7 +93,7 @@
bal = get_balance_on(stock_in_hand_account, dn.doc.posting_date)
self.assertEquals(bal, prev_bal - 375.0)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
test_records = [
[
diff --git a/stock/doctype/material_request/test_material_request.py b/stock/doctype/material_request/test_material_request.py
index 80233d7..1040d64 100644
--- a/stock/doctype/material_request/test_material_request.py
+++ b/stock/doctype/material_request/test_material_request.py
@@ -10,7 +10,7 @@
class TestMaterialRequest(unittest.TestCase):
def setUp(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
def test_make_purchase_order(self):
from stock.doctype.material_request.material_request import make_purchase_order
diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py
index 8416747..1f554c8 100644
--- a/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -127,7 +127,7 @@
self.validate_inspection()
self.validate_uom_is_integer("uom", ["qty", "received_qty"])
self.validate_uom_is_integer("stock_uom", "stock_qty")
-
+
get_obj('Stock Ledger').validate_serial_no(self, 'purchase_receipt_details')
self.validate_challan_no()
@@ -157,14 +157,12 @@
d.rejected_serial_no = cstr(d.rejected_serial_no).strip().replace(',', '\n')
d.save()
- def update_stock(self, is_submit):
+ def update_stock(self):
pc_obj = get_obj('Purchase Common')
- self.values = []
+ sl_entries = []
+ stock_items = self.get_stock_items()
for d in getlist(self.doclist, 'purchase_receipt_details'):
- if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == "Yes":
- if not d.warehouse:
- continue
-
+ if d.item_code in stock_items and d.warehouse:
ord_qty = 0
pr_qty = flt(d.qty) * flt(d.conversion_factor)
@@ -187,51 +185,30 @@
args = {
"item_code": d.item_code,
"posting_date": self.doc.posting_date,
- "ordered_qty": (is_submit and 1 or -1) * flt(ord_qty)
+ "ordered_qty": (self.doc.docstatus==1 and 1 or -1) * flt(ord_qty)
}
get_obj("Warehouse", d.warehouse).update_bin(args)
- # UPDATE actual qty to warehouse by pr_qty
if pr_qty:
- self.make_sl_entry(d, d.warehouse, flt(pr_qty), d.valuation_rate, is_submit)
-
- # UPDATE actual to rejected warehouse by rejected qty
+ sl_entries.append(self.get_sl_entries(d, {
+ "actual_qty": flt(pr_qty),
+ "serial_no": cstr(d.serial_no).strip(),
+ "incoming_rate": d.valuation_rate
+
+ }))
+
if flt(d.rejected_qty) > 0:
- self.make_sl_entry(d, self.doc.rejected_warehouse, flt(d.rejected_qty) * flt(d.conversion_factor), d.valuation_rate, is_submit, rejected = 1)
+ sl_entries.append(self.get_sl_entries(d, {
+ "warehouse": self.doc.rejected_warehouse,
+ "actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
+ "serial_no": cstr(d.rejected_serial_no).strip(),
+ "incoming_rate": d.valuation_rate
+ }))
- self.bk_flush_supp_wh(is_submit)
-
- if self.values:
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values)
-
-
- # make Stock Entry
- def make_sl_entry(self, d, wh, qty, in_value, is_submit, rejected = 0):
- if rejected:
- serial_no = cstr(d.rejected_serial_no).strip()
- else:
- serial_no = cstr(d.serial_no).strip()
-
- self.values.append({
- 'item_code' : d.fields.has_key('item_code') and d.item_code or d.rm_item_code,
- 'warehouse' : wh,
- 'posting_date' : self.doc.posting_date,
- 'posting_time' : self.doc.posting_time,
- 'voucher_type' : 'Purchase Receipt',
- 'voucher_no' : self.doc.name,
- 'voucher_detail_no' : d.name,
- 'actual_qty' : qty,
- 'stock_uom' : d.stock_uom,
- 'incoming_rate' : in_value,
- 'company' : self.doc.company,
- 'fiscal_year' : self.doc.fiscal_year,
- 'is_cancelled' : (is_submit==1) and 'No' or 'Yes',
- 'batch_no' : cstr(d.batch_no).strip(),
- 'serial_no' : serial_no,
- "project" : d.project_name
- })
-
-
+ self.bk_flush_supp_wh(sl_entries)
+
+ self.make_sl_entries(sl_entries)
+
def validate_inspection(self):
for d in getlist(self.doclist, 'purchase_receipt_details'): #Enter inspection date for all items that require inspection
ins_reqd = sql("select inspection_required from `tabItem` where name = %s",
@@ -265,7 +242,7 @@
get_obj('Stock Ledger').update_serial_record(self, 'purchase_receipt_details', is_submit = 1, is_incoming = 1)
# Update Stock
- self.update_stock(is_submit = 1)
+ self.update_stock()
# Update last purchase rate
purchase_controller.update_last_purchase_rate(self, 1)
@@ -297,23 +274,23 @@
# 3. Cancel Serial No
get_obj('Stock Ledger').update_serial_record(self, 'purchase_receipt_details', is_submit = 0, is_incoming = 1)
- # 4.Update Bin
- self.update_stock(is_submit = 0)
-
+ self.update_stock()
self.update_prevdoc_status()
-
- # 6. Update last purchase rate
pc_obj.update_last_purchase_rate(self, 0)
self.make_cancel_gl_entries()
- def bk_flush_supp_wh(self, is_submit):
+ def bk_flush_supp_wh(self, sl_entries):
for d in getlist(self.doclist, 'pr_raw_material_details'):
# negative quantity is passed as raw material qty has to be decreased
# when PR is submitted and it has to be increased when PR is cancelled
- consumed_qty = - flt(d.consumed_qty)
- self.make_sl_entry(d, self.doc.supplier_warehouse, flt(consumed_qty), 0, is_submit)
-
+ sl_entries.append(self.get_sl_entries(d, {
+ "item_code": d.rm_item_code,
+ "warehouse": self.doc.supplier_warehouse,
+ "actual_qty": -1*flt(consumed_qty),
+ "incoming_rate": 0
+ }))
+
def get_current_stock(self):
for d in getlist(self.doclist, 'pr_raw_material_details'):
if self.doc.supplier_warehouse:
@@ -325,28 +302,28 @@
return get_obj('Purchase Common').get_rate(arg,self)
def make_gl_entries(self):
- if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")):
+ if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
return
- from accounts.general_ledger import make_gl_entries
-
against_stock_account = self.get_company_default("stock_received_but_not_billed")
- total_valuation_amount = self.get_total_valuation_amount()
- gl_entries = self.get_gl_entries_for_stock(against_stock_account, total_valuation_amount)
+ stock_items = self.get_stock_items()
+
+ 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))
-
- def get_total_valuation_amount(self):
- total_valuation_amount = 0.0
-
- for item in self.doclist.get({"parentfield": "purchase_receipt_details"}):
- if item.item_code in self.stock_items:
- total_valuation_amount += flt(item.valuation_rate) * \
- flt(item.qty) * flt(item.conversion_factor)
-
- return total_valuation_amount
-
+ self.sync_stock_account_balance(warehouse_list)
@webnotes.whitelist()
def make_purchase_invoice(source_name, target_doclist=None):
diff --git a/stock/doctype/purchase_receipt/test_purchase_receipt.py b/stock/doctype/purchase_receipt/test_purchase_receipt.py
index c871b36..f5320c1 100644
--- a/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -10,6 +10,8 @@
class TestPurchaseReceipt(unittest.TestCase):
def test_make_purchase_invoice(self):
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
+ self._clear_stock_account_balance()
from stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
pr = webnotes.bean(copy=test_records[0]).insert()
@@ -29,8 +31,9 @@
self.assertRaises(webnotes.ValidationError, webnotes.bean(pi).submit)
def test_purchase_receipt_no_gl_entry(self):
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
+ self._clear_stock_account_balance()
pr = webnotes.bean(copy=test_records[0])
- pr.run_method("calculate_taxes_and_totals")
pr.insert()
pr.submit()
@@ -41,11 +44,12 @@
self.assertTrue(not gl_entries)
def test_purchase_receipt_gl_entry(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
- self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 1)
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
+ self.assertEqual(cint(webnotes.defaults.get_global_default("perpetual_accounting")), 1)
+
+ self._clear_stock_account_balance()
pr = webnotes.bean(copy=test_records[0])
- pr.run_method("calculate_taxes_and_totals")
pr.insert()
pr.submit()
@@ -54,20 +58,28 @@
order by account desc""", pr.doc.name, as_dict=1)
self.assertTrue(gl_entries)
- stock_in_hand_account = webnotes.conn.get_value("Company", pr.doc.company,
- "stock_in_hand_account")
+ stock_in_hand_account = webnotes.conn.get_value("Warehouse", pr.doclist[1].warehouse,
+ "account")
- expected_values = [
- [stock_in_hand_account, 750.0, 0.0],
- ["Stock Received But Not Billed - _TC", 0.0, 750.0]
- ]
+ fixed_asset_account = webnotes.conn.get_value("Warehouse", pr.doclist[2].warehouse,
+ "account")
- for i, gle in enumerate(gl_entries):
- self.assertEquals(expected_values[i][0], gle.account)
- self.assertEquals(expected_values[i][1], gle.debit)
- self.assertEquals(expected_values[i][2], gle.credit)
+ expected_values = {
+ stock_in_hand_account: [375.0, 0.0],
+ fixed_asset_account: [375.0, 0.0],
+ "Stock Received But Not Billed - _TC": [0.0, 750.0]
+ }
+
+ for gle in gl_entries:
+ self.assertEquals(expected_values[gle.account][0], gle.debit)
+ self.assertEquals(expected_values[gle.account][1], gle.credit)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
+
+ def _clear_stock_account_balance(self):
+ webnotes.conn.sql("delete from `tabStock Ledger Entry`")
+ webnotes.conn.sql("""delete from `tabBin`""")
+ webnotes.conn.sql("""delete from `tabGL Entry`""")
def test_subcontracting(self):
pr = webnotes.bean(copy=test_records[1])
@@ -102,16 +114,32 @@
"item_code": "_Test Item",
"item_name": "_Test Item",
"parentfield": "purchase_receipt_details",
- "received_qty": 10.0,
- "qty": 10.0,
+ "received_qty": 5.0,
+ "qty": 5.0,
"rejected_qty": 0.0,
"import_rate": 50.0,
- "amount": 500.0,
+ "amount": 250.0,
"warehouse": "_Test Warehouse - _TC",
"stock_uom": "Nos",
"uom": "_Test UOM",
},
{
+ "conversion_factor": 1.0,
+ "description": "_Test Item",
+ "doctype": "Purchase Receipt Item",
+ "item_code": "_Test Item",
+ "item_name": "_Test Item",
+ "parentfield": "purchase_receipt_details",
+ "received_qty": 5.0,
+ "qty": 5.0,
+ "rejected_qty": 0.0,
+ "import_rate": 50.0,
+ "amount": 250.0,
+ "warehouse": "_Test Warehouse 1 - _TC",
+ "stock_uom": "Nos",
+ "uom": "_Test UOM",
+ },
+ {
"account_head": "_Test Account Shipping Charges - _TC",
"add_deduct_tax": "Add",
"category": "Valuation and Total",
diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py
index 7e59d64..83397a6 100644
--- a/stock/doctype/serial_no/serial_no.py
+++ b/stock/doctype/serial_no/serial_no.py
@@ -61,9 +61,8 @@
self.make_gl_entries()
- def make_stock_ledger_entry(self, qty):
- from webnotes.model.code import get_obj
- values = [{
+ def make_stock_ledger_entry(self, qty):
+ sl_entries = [{
'item_code' : self.doc.item_code,
'warehouse' : self.doc.warehouse,
'posting_date' : self.doc.purchase_date or (self.doc.creation and self.doc.creation.split(' ')[0]) or nowdate(),
@@ -80,8 +79,8 @@
'batch_no' : '',
'serial_no' : self.doc.name
}]
- get_obj('Stock Ledger').update_stock(values)
-
+
+ self.make_sl_entries(sl_entries)
def on_trash(self):
if self.doc.status == 'Delivered':
@@ -90,7 +89,7 @@
webnotes.conn.set(self.doc, 'status', 'Not in Use')
self.make_stock_ledger_entry(-1)
- if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \
+ if cint(webnotes.defaults.get_global_default("perpetual_accounting")) \
and webnotes.conn.sql("""select name from `tabGL Entry`
where voucher_type=%s and voucher_no=%s and ifnull(is_cancelled, 'No')='No'""",
(self.doc.doctype, self.doc.name)):
@@ -121,16 +120,24 @@
('\n'.join(serial_nos), item[0]))
def make_gl_entries(self, cancel=False):
- if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")):
+ if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
return
-
- from accounts.general_ledger import make_gl_entries
- against_stock_account = self.get_company_default("stock_adjustment_account")
- gl_entries = self.get_gl_entries_for_stock(against_stock_account, self.doc.purchase_rate)
-
- for entry in gl_entries:
- entry["posting_date"] = self.doc.purchase_date or (self.doc.creation and
- self.doc.creation.split(' ')[0]) or nowdate()
+ if not self.doc.cost_center:
+ msgprint(_("Please enter Cost Center"), raise_exception=1)
+
+ against_stock_account = self.get_company_default("stock_adjustment_account")
+ gl_entries = self.get_gl_entries_for_stock(against_stock_account,
+ self.doc.purchase_rate, self.doc.warehouse, cost_center=self.doc.cost_center)
+
+ posting_date = self.doc.purchase_date or (self.doc.creation and
+ self.doc.creation.split(' ')[0]) or nowdate()
+
+ for entry in gl_entries:
+ entry["posting_date"] = posting_date
+
if gl_entries:
- make_gl_entries(gl_entries, cancel)
\ No newline at end of file
+ from accounts.general_ledger import make_gl_entries
+ make_gl_entries(gl_entries, cancel)
+ self.sync_stock_account_balance([self.doc.warehouse], self.doc.cost_center,
+ posting_date)
diff --git a/stock/doctype/serial_no/serial_no.txt b/stock/doctype/serial_no/serial_no.txt
index efa35f5..6fd1979 100644
--- a/stock/doctype/serial_no/serial_no.txt
+++ b/stock/doctype/serial_no/serial_no.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-16 10:59:15",
"docstatus": 0,
- "modified": "2013-07-22 15:29:43",
+ "modified": "2013-08-05 17:35:10",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -151,6 +151,14 @@
"search_index": 0
},
{
+ "depends_on": "eval:sys_defaults.perpetual_accounting",
+ "doctype": "DocField",
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
"doctype": "DocField",
"fieldname": "purchase_details",
"fieldtype": "Section Break",
@@ -524,6 +532,13 @@
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
+ "role": "System Manager",
+ "write": 1
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "doctype": "DocPerm",
"role": "Material Master Manager",
"write": 1
},
diff --git a/stock/doctype/serial_no/test_serial_no.py b/stock/doctype/serial_no/test_serial_no.py
index 1898b2f..4657ff2 100644
--- a/stock/doctype/serial_no/test_serial_no.py
+++ b/stock/doctype/serial_no/test_serial_no.py
@@ -9,8 +9,7 @@
class TestSerialNo(unittest.TestCase):
def test_aii_gl_entries_for_serial_no_in_store(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
-
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
sr = webnotes.bean(copy=test_records[0])
sr.doc.serial_no = "_Test Serial No 1"
sr.insert()
@@ -67,11 +66,11 @@
self.assertEquals(gl_count[0][0], 4)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
def test_aii_gl_entries_for_serial_no_delivered(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
sr = webnotes.bean(copy=test_records[0])
sr.doc.serial_no = "_Test Serial No 2"
@@ -83,7 +82,7 @@
order by account desc""", sr.doc.name, as_dict=1)
self.assertFalse(gl_entries)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
test_dependencies = ["Item"]
test_records = [
@@ -99,7 +98,8 @@
"purchase_rate": 1000.0,
"purchase_time": "11:37:39",
"purchase_date": "2013-02-26",
- 'fiscal_year': "_Test Fiscal Year 2013"
+ 'fiscal_year': "_Test Fiscal Year 2013",
+ "cost_center": "_Test Cost Center - _TC"
}
]
]
\ No newline at end of file
diff --git a/stock/doctype/stock_entry/stock_entry.js b/stock/doctype/stock_entry/stock_entry.js
index 4fa6c6c..21829b9 100644
--- a/stock/doctype/stock_entry/stock_entry.js
+++ b/stock/doctype/stock_entry/stock_entry.js
@@ -12,9 +12,9 @@
set_default_account: function() {
var me = this;
- if (cint(wn.defaults.get_default("auto_inventory_accounting")) && !this.frm.doc.expense_adjustment_account) {
+ if (cint(wn.defaults.get_default("perpetual_accounting")) && !this.frm.doc.expense_adjustment_account) {
if (this.frm.doc.purpose == "Sales Return")
- account_for = "stock_in_hand_account";
+ account_for = "default_expense_account";
else if (this.frm.doc.purpose == "Purchase Return")
account_for = "stock_received_but_not_billed";
else account_for = "stock_adjustment_account";
@@ -65,7 +65,7 @@
}
};
- if(cint(wn.defaults.get_default("auto_inventory_accounting"))) {
+ if(cint(wn.defaults.get_default("perpetual_accounting"))) {
this.frm.add_fetch("company", "stock_adjustment_account", "expense_adjustment_account");
this.frm.fields_dict["expense_adjustment_account"].get_query = function() {
diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py
index 66d1dbf..9dae258 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -38,7 +38,6 @@
self.validate_item()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "transfer_qty")
-
self.validate_warehouse(pro_obj)
self.validate_production_order(pro_obj)
self.get_stock_and_rate()
@@ -52,13 +51,13 @@
def on_submit(self):
self.update_serial_no(1)
- self.update_stock_ledger(0)
+ self.update_stock_ledger()
self.update_production_order(1)
self.make_gl_entries()
def on_cancel(self):
self.update_serial_no(0)
- self.update_stock_ledger(1)
+ self.update_stock_ledger()
self.update_production_order(0)
self.make_cancel_gl_entries()
@@ -80,8 +79,9 @@
sl_obj.validate_serial_no(self, 'mtn_details')
def validate_item(self):
+ stock_items = self.get_stock_items()
for item in self.doclist.get({"parentfield": "mtn_details"}):
- if item.item_code not in self.stock_items:
+ if item.item_code not in stock_items:
msgprint(_("""Only Stock Items are allowed for Stock Entry"""),
raise_exception=True)
@@ -170,32 +170,41 @@
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("auto_inventory_accounting")):
+ if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
return
- if not self.doc.expense_adjustment_account:
- webnotes.msgprint(_("Please enter Expense/Adjustment Account"), raise_exception=1)
-
- from accounts.general_ledger import make_gl_entries
-
- total_valuation_amount = self.get_total_valuation_amount()
-
- gl_entries = self.get_gl_entries_for_stock(self.doc.expense_adjustment_account,
- total_valuation_amount)
- if gl_entries:
- make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2)
-
- def get_total_valuation_amount(self):
- total_valuation_amount = 0
+ gl_entries = []
+ warehouse_list = []
+ against_expense_account = self.doc.expense_adjustment_account
for item in self.doclist.get({"parentfield": "mtn_details"}):
- if item.t_warehouse and not item.s_warehouse:
- total_valuation_amount += flt(item.incoming_rate, 2) * flt(item.transfer_qty)
+ 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 item.s_warehouse and not item.t_warehouse:
- total_valuation_amount -= flt(item.incoming_rate, 2) * flt(item.transfer_qty)
-
- return total_valuation_amount
+ 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'):
@@ -338,16 +347,24 @@
serial_doc.docstatus = is_submit and 2 or 0
serial_doc.save()
- def update_stock_ledger(self, is_cancelled=0):
- self.values = []
+ def update_stock_ledger(self):
+ sl_entries = []
for d in getlist(self.doclist, 'mtn_details'):
if cstr(d.s_warehouse):
- self.add_to_values(d, cstr(d.s_warehouse), -flt(d.transfer_qty), is_cancelled)
+ sl_entries.append(self.get_sl_entries(d, {
+ "warehouse": cstr(d.s_warehouse),
+ "actual_qty": -flt(d.transfer_qty),
+ "incoming_rate": flt(d.incoming_rate)
+ }))
+
if cstr(d.t_warehouse):
- self.add_to_values(d, cstr(d.t_warehouse), flt(d.transfer_qty), is_cancelled)
-
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values,
- self.doc.amended_from and 'Yes' or 'No')
+ sl_entries.append(self.get_sl_entries(d, {
+ "warehouse": cstr(d.t_warehouse),
+ "actual_qty": flt(d.transfer_qty),
+ "incoming_rate": flt(d.incoming_rate)
+ }))
+
+ self.make_sl_entries(sl_entries, self.doc.amended_from and 'Yes' or 'No')
def update_production_order(self, is_submit):
if self.doc.production_order:
@@ -625,26 +642,6 @@
# to be assigned for finished item
se_child.bom_no = bom_no
- def add_to_values(self, d, wh, qty, is_cancelled):
- self.values.append({
- 'item_code': d.item_code,
- 'warehouse': wh,
- 'posting_date': self.doc.posting_date,
- 'posting_time': self.doc.posting_time,
- 'voucher_type': 'Stock Entry',
- 'voucher_no': self.doc.name,
- 'voucher_detail_no': d.name,
- 'actual_qty': qty,
- 'incoming_rate': flt(d.incoming_rate, 2) or 0,
- 'stock_uom': d.stock_uom,
- 'company': self.doc.company,
- 'is_cancelled': (is_cancelled ==1) and 'Yes' or 'No',
- 'batch_no': cstr(d.batch_no).strip(),
- 'serial_no': cstr(d.serial_no).strip(),
- "project": self.doc.project_name,
- "fiscal_year": self.doc.fiscal_year,
- })
-
def get_cust_values(self):
"""fetches customer details"""
if self.doc.delivery_note_no:
diff --git a/stock/doctype/stock_entry/stock_entry.txt b/stock/doctype/stock_entry/stock_entry.txt
index dac6e70..49f4765 100644
--- a/stock/doctype/stock_entry/stock_entry.txt
+++ b/stock/doctype/stock_entry/stock_entry.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-04-09 11:43:55",
"docstatus": 0,
- "modified": "2013-08-05 15:28:09",
+ "modified": "2013-08-05 17:36:25",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -200,7 +200,7 @@
"search_index": 0
},
{
- "depends_on": "eval:sys_defaults.auto_inventory_accounting",
+ "depends_on": "eval:sys_defaults.perpetual_accounting",
"doctype": "DocField",
"fieldname": "expense_adjustment_account",
"fieldtype": "Link",
@@ -210,6 +210,14 @@
"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",
"fieldtype": "Section Break",
diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py
index 6438116..b2b8bfc 100644
--- a/stock/doctype/stock_entry/test_stock_entry.py
+++ b/stock/doctype/stock_entry/test_stock_entry.py
@@ -10,14 +10,14 @@
class TestStockEntry(unittest.TestCase):
def tearDown(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
if hasattr(self, "old_default_company"):
webnotes.conn.set_default("company", self.old_default_company)
def test_auto_material_request(self):
webnotes.conn.sql("""delete from `tabMaterial Request Item`""")
webnotes.conn.sql("""delete from `tabMaterial Request`""")
- self._clear_stock()
+ self._clear_stock_account_balance()
webnotes.conn.set_value("Stock Settings", None, "auto_indent", True)
@@ -47,15 +47,15 @@
self.assertRaises(InvalidWarehouseCompany, st1.submit)
def test_material_receipt_gl_entry(self):
- webnotes.conn.sql("delete from `tabStock Ledger Entry`")
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ self._clear_stock_account_balance()
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
mr = webnotes.bean(copy=test_records[0])
mr.insert()
mr.submit()
- stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company",
- "stock_in_hand_account")
+ stock_in_hand_account = webnotes.conn.get_value("Warehouse", mr.doclist[1].t_warehouse,
+ "account")
self.check_stock_ledger_entries("Stock Entry", mr.doc.name,
[["_Test Item", "_Test Warehouse - _TC", 50.0]])
@@ -72,18 +72,15 @@
sorted([["_Test Item", "_Test Warehouse - _TC", 50.0],
["_Test Item", "_Test Warehouse - _TC", -50.0]]))
- self.check_gl_entries("Stock Entry", mr.doc.name,
- sorted([
- [stock_in_hand_account, 5000.0, 0.0],
- ["Stock Adjustment - _TC", 0.0, 5000.0],
- [stock_in_hand_account, 0.0, 5000.0],
- ["Stock Adjustment - _TC", 5000.0, 0.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)
+
def test_material_issue_gl_entry(self):
- self._clear_stock()
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ self._clear_stock_account_balance()
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
mr = webnotes.bean(copy=test_records[0])
mr.insert()
@@ -93,12 +90,11 @@
mi.insert()
mi.submit()
- stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company",
- "stock_in_hand_account")
-
self.check_stock_ledger_entries("Stock Entry", mi.doc.name,
[["_Test Item", "_Test Warehouse - _TC", -40.0]])
-
+
+ stock_in_hand_account = webnotes.conn.get_value("Warehouse", mi.doclist[1].s_warehouse,
+ "account")
self.check_gl_entries("Stock Entry", mi.doc.name,
sorted([
[stock_in_hand_account, 0.0, 4000.0],
@@ -111,23 +107,27 @@
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.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)
- self.check_gl_entries("Stock Entry", mi.doc.name,
- sorted([
- [stock_in_hand_account, 0.0, 4000.0],
- ["Stock Adjustment - _TC", 4000.0, 0.0],
- [stock_in_hand_account, 4000.0, 0.0],
- ["Stock Adjustment - _TC", 0.0, 4000.0],
- ])
- )
+ 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 test_material_transfer_gl_entry(self):
- self._clear_stock()
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ self._clear_stock_account_balance()
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
mr = webnotes.bean(copy=test_records[0])
mr.insert()
mr.submit()
+
+
mtn = webnotes.bean(copy=test_records[2])
mtn.insert()
@@ -136,10 +136,18 @@
self.check_stock_ledger_entries("Stock Entry", mtn.doc.name,
[["_Test Item", "_Test Warehouse - _TC", -45.0], ["_Test Item", "_Test Warehouse 1 - _TC", 45.0]])
- # no gl entry
- gl_entries = webnotes.conn.sql("""select * from `tabGL Entry`
- where voucher_type = 'Stock Entry' and voucher_no=%s""", mtn.doc.name)
- self.assertFalse(gl_entries)
+ stock_in_hand_account = webnotes.conn.get_value("Warehouse", mtn.doclist[1].s_warehouse,
+ "account")
+ fixed_asset_account = webnotes.conn.get_value("Warehouse", mtn.doclist[1].t_warehouse,
+ "account")
+
+ self.check_gl_entries("Stock Entry", mtn.doc.name,
+ sorted([
+ [stock_in_hand_account, 0.0, 4500.0],
+ [fixed_asset_account, 4500.0, 0.0],
+ ])
+ )
+
mtn.cancel()
self.check_stock_ledger_entries("Stock Entry", mtn.doc.name,
@@ -148,10 +156,7 @@
["_Test Item", "_Test Warehouse - _TC", -45.0],
["_Test Item", "_Test Warehouse 1 - _TC", 45.0]]))
- # no gl entry
- gl_entries = webnotes.conn.sql("""select * from `tabGL Entry`
- where voucher_type = 'Stock Entry' and voucher_no=%s""", mtn.doc.name)
- self.assertFalse(gl_entries)
+
def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle):
# check stock ledger entries
@@ -185,7 +190,7 @@
webnotes.conn.set_default("company", "_Test Company")
def _insert_material_receipt(self):
- self._clear_stock()
+ self._clear_stock_account_balance()
se1 = webnotes.bean(copy=test_records[0])
se1.insert()
se1.submit()
@@ -286,7 +291,6 @@
from stock.doctype.delivery_note.delivery_note import make_sales_invoice
actual_qty_0 = self._get_actual_qty()
-
# make a delivery note based on this invoice
dn = webnotes.bean(copy=delivery_note_test_records[0])
dn.doclist[1].item_code = item_code
@@ -417,7 +421,7 @@
return se
def test_purchase_receipt_return(self):
- self._clear_stock()
+ self._clear_stock_account_balance()
actual_qty_0 = self._get_actual_qty()
@@ -433,7 +437,7 @@
actual_qty_1 = self._get_actual_qty()
- self.assertEquals(actual_qty_0 + 10, actual_qty_1)
+ self.assertEquals(actual_qty_0 + 5, actual_qty_1)
pi_doclist = make_purchase_invoice(pr.doc.name)
@@ -507,7 +511,7 @@
self._test_purchase_return_jv(se)
def _test_purchase_return_return_against_purchase_order(self):
- self._clear_stock()
+ self._clear_stock_account_balance()
actual_qty_0 = self._get_actual_qty()
@@ -571,6 +575,14 @@
return se, pr.doc.name
+ def _clear_stock_account_balance(self):
+ webnotes.conn.sql("delete from `tabStock Ledger Entry`")
+ webnotes.conn.sql("""delete from `tabBin`""")
+ webnotes.conn.sql("""delete from `tabGL Entry`""")
+
+ self.old_default_company = webnotes.conn.get_default("company")
+ webnotes.conn.set_default("company", "_Test Company")
+
test_records = [
[
{
@@ -580,7 +592,8 @@
"posting_time": "17:14:24",
"purpose": "Material Receipt",
"fiscal_year": "_Test Fiscal Year 2013",
- "expense_adjustment_account": "Stock Adjustment - _TC"
+ "expense_adjustment_account": "Stock Adjustment - _TC",
+ "cost_center": "_Test Cost Center - _TC"
},
{
"conversion_factor": 1.0,
@@ -603,7 +616,8 @@
"posting_time": "17:15",
"purpose": "Material Issue",
"fiscal_year": "_Test Fiscal Year 2013",
- "expense_adjustment_account": "Stock Adjustment - _TC"
+ "expense_adjustment_account": "Stock Adjustment - _TC",
+ "cost_center": "_Test Cost Center - _TC"
},
{
"conversion_factor": 1.0,
@@ -626,7 +640,8 @@
"posting_time": "17:14:24",
"purpose": "Material Transfer",
"fiscal_year": "_Test Fiscal Year 2013",
- "expense_adjustment_account": "Stock Adjustment - _TC"
+ "expense_adjustment_account": "Stock Adjustment - _TC",
+ "cost_center": "_Test Cost Center - _TC"
},
{
"conversion_factor": 1.0,
diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py
index ed8196a..77ec060 100644
--- a/stock/doctype/stock_ledger/stock_ledger.py
+++ b/stock/doctype/stock_ledger/stock_ledger.py
@@ -177,7 +177,7 @@
self.update_serial_purchase_details(obj, d, a, is_submit, rejected=True)
- def update_stock(self, values, is_amended = 'No'):
+ def update_stock(self, values, is_amended='No'):
for v in values:
sle_id, valid_serial_nos = '', ''
# get serial nos
diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.js b/stock/doctype/stock_reconciliation/stock_reconciliation.js
index fa2600e..4a66c3c 100644
--- a/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -12,7 +12,7 @@
set_default_expense_account: function() {
var me = this;
- if (sys_defaults.auto_inventory_accounting && !this.frm.doc.expense_account) {
+ if (sys_defaults.perpetual_accounting && !this.frm.doc.expense_account) {
return this.frm.call({
method: "accounts.utils.get_company_default",
args: {
@@ -28,7 +28,7 @@
setup: function() {
var me = this;
- if (sys_defaults.auto_inventory_accounting) {
+ if (sys_defaults.perpetual_accounting) {
this.frm.add_fetch("company", "stock_adjustment_account", "expense_account");
this.frm.fields_dict["expense_account"].get_query = function() {
diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.py b/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 36f872e..fc3b1bd 100644
--- a/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -56,7 +56,6 @@
if len(rows) > 100:
msgprint(_("""Sorry! We can only allow upto 100 rows for Stock Reconciliation."""),
raise_exception=True)
-
for row_num, row in enumerate(rows):
# find duplicates
if [row[0], row[1]] in item_warehouse_combinations:
@@ -88,7 +87,7 @@
msgprint(msg)
raise webnotes.ValidationError
-
+
def validate_item(self, item_code, row_num):
from stock.utils import validate_end_of_life, validate_is_stock_item, \
validate_cancelled_item
@@ -288,26 +287,54 @@
warehouse_list = [d.warehouse for d in self.entries]
stock_ledger_entries = self.get_stock_ledger_entries(item_list, warehouse_list)
- self.doc.stock_value_difference = 0.0
+ stock_value_difference = {}
for d in self.entries:
- self.doc.stock_value_difference -= get_buying_amount(d.item_code, d.warehouse,
- d.actual_qty, self.doc.doctype, self.doc.name, d.voucher_detail_no,
- stock_ledger_entries)
- webnotes.conn.set(self.doc, "stock_value_difference", self.doc.stock_value_difference)
+ diff = get_buying_amount(d.item_code, d.warehouse, d.actual_qty, self.doc.doctype,
+ self.doc.name, d.voucher_detail_no, stock_ledger_entries)
+ stock_value_difference.setdefault(d.warehouse, 0.0)
+ 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("auto_inventory_accounting")):
+ if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
return
if not self.doc.expense_account:
msgprint(_("Please enter Expense Account"), raise_exception=1)
+ else:
+ self.validate_expense_account()
- from accounts.general_ledger import make_gl_entries
-
- gl_entries = self.get_gl_entries_for_stock(self.doc.expense_account,
- self.doc.stock_value_difference)
+ if not self.doc.cost_center:
+ msgprint(_("Please enter Cost Center"), raise_exception=1)
+
+ 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)
+ print webnotes.conn.sql("""select name, posting_date, stock_value from `tabStock Ledger Entry`""")
+ print webnotes.conn.sql("""select stock_value from tabBin""")
+ self.sync_stock_account_balance(warehouse_list, self.doc.cost_center)
+
+ def validate_expense_account(self):
+ if not webnotes.conn.sql("select * from `tabStock Ledger Entry`"):
+ if webnotes.conn.get_value("Account", self.doc.expense_account,
+ "is_pl_account") == "Yes":
+ msgprint(_("""Expense Account can not be a PL Account, as this stock \
+ reconciliation is an opening entry. Please select 'Temporary Liability' or \
+ relevant account"""), raise_exception=1)
+
@webnotes.whitelist()
def upload():
diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.txt b/stock/doctype/stock_reconciliation/stock_reconciliation.txt
index 7ddcbf7..a00547c 100644
--- a/stock/doctype/stock_reconciliation/stock_reconciliation.txt
+++ b/stock/doctype/stock_reconciliation/stock_reconciliation.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-03-28 10:35:31",
"docstatus": 0,
- "modified": "2013-07-22 15:22:44",
+ "modified": "2013-08-07 11:14:17",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -151,7 +151,7 @@
{
"doctype": "DocField",
"fieldname": "stock_value_difference",
- "fieldtype": "Currency",
+ "fieldtype": "Long Text",
"hidden": 1,
"in_list_view": 1,
"label": "Stock Value Difference",
diff --git a/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 909dfc7..b7dae32 100644
--- a/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -8,11 +8,12 @@
import webnotes, unittest
from webnotes.utils import flt
import json
-from accounts.utils import get_fiscal_year
+from accounts.utils import get_fiscal_year, get_stock_and_account_difference, get_balance_on
+
class TestStockReconciliation(unittest.TestCase):
- def test_reco_for_fifo(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ 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]]
input_data = [
@@ -55,8 +56,8 @@
self.assertFalse(gl_entries)
- def test_reco_for_moving_average(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ def atest_reco_for_moving_average(self):
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
# [[qty, valuation_rate, posting_date,
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
input_data = [
@@ -102,10 +103,9 @@
self.assertFalse(gl_entries)
def test_reco_fifo_gl_entries(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
- # [[qty, valuation_rate, posting_date,
- # posting_time, stock_in_hand_debit]]
+ # [[qty, valuation_rate, posting_date, posting_time, stock_in_hand_debit]]
input_data = [
[50, 1000, "2012-12-26", "12:00", 38000],
[5, 1000, "2012-12-26", "12:00", -7000],
@@ -123,21 +123,23 @@
]
for d in input_data:
+ # print d[0], d[1], d[2], d[3]
self.cleanup_data()
self.insert_existing_sle("FIFO")
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
# check gl_entries
- self.check_gl_entries(stock_reco.doc.name, d[4])
-
+ self.assertFalse(get_stock_and_account_difference(["_Test Warehouse - _TC"]))
+ print get_balance_on("_Test Account Stock In Hand - _TC")
+ self.assertEquals(get_balance_on("_Test Account Stock In Hand - _TC", d[2]), 38000)
# cancel
stock_reco.cancel()
- self.check_gl_entries(stock_reco.doc.name, -d[4], True)
+ # self.check_gl_entries(stock_reco.doc.name, -d[4], True)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
- def test_reco_moving_average_gl_entries(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ def atest_reco_moving_average_gl_entries(self):
+ webnotes.defaults.set_global_default("perpetual_accounting", 1)
# [[qty, valuation_rate, posting_date,
# posting_time, stock_in_hand_debit]]
@@ -169,12 +171,13 @@
stock_reco.cancel()
self.check_gl_entries(stock_reco.doc.name, -d[4], True)
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+ webnotes.defaults.set_global_default("perpetual_accounting", 0)
def cleanup_data(self):
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
webnotes.conn.sql("delete from tabBin")
+ webnotes.conn.sql("delete from `tabGL Entry`")
def submit_stock_reconciliation(self, qty, rate, posting_date, posting_time):
stock_reco = webnotes.bean([{
@@ -184,6 +187,7 @@
"fiscal_year": get_fiscal_year(posting_date)[0],
"company": "_Test Company",
"expense_account": "Stock Adjustment - _TC",
+ "cost_center": "_Test Cost Center - _TC",
"reconciliation_json": json.dumps([
["Item Code", "Warehouse", "Quantity", "Valuation Rate"],
["_Test Item", "_Test Warehouse - _TC", qty, rate]
@@ -194,32 +198,35 @@
return stock_reco
def check_gl_entries(self, voucher_no, stock_value_diff, cancel=None):
- stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company",
- "stock_in_hand_account")
+ stock_in_hand_account = webnotes.conn.get_value("Warehouse", "_Test Warehouse - _TC",
+ "account")
debit_amount = stock_value_diff > 0 and stock_value_diff or 0.0
credit_amount = stock_value_diff < 0 and abs(stock_value_diff) or 0.0
- expected_gl_entries = sorted([
+ expected_gl_entries = [
[stock_in_hand_account, debit_amount, credit_amount],
["Stock Adjustment - _TC", credit_amount, debit_amount]
- ])
+ ]
if cancel:
- expected_gl_entries = sorted([
+ expected_gl_entries = [
[stock_in_hand_account, debit_amount, credit_amount],
["Stock Adjustment - _TC", credit_amount, debit_amount],
[stock_in_hand_account, credit_amount, debit_amount],
["Stock Adjustment - _TC", debit_amount, credit_amount]
- ])
+ ]
+ expected_gl_entries.sort(key=lambda x: x[0])
gl_entries = webnotes.conn.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Stock Reconciliation' and voucher_no=%s
- order by account asc, debit asc""", voucher_no, as_dict=1)
+ order by account asc, name asc""", voucher_no, as_list=1)
self.assertTrue(gl_entries)
+ gl_entries.sort(key=lambda x: x[0])
+ print gl_entries
for i, gle in enumerate(gl_entries):
- self.assertEquals(expected_gl_entries[i][0], gle.account)
- self.assertEquals(expected_gl_entries[i][1], gle.debit)
- self.assertEquals(expected_gl_entries[i][2], gle.credit)
+ 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 insert_existing_sle(self, valuation_method):
webnotes.conn.set_value("Item", "_Test Item", "valuation_method", valuation_method)
diff --git a/stock/doctype/warehouse/test_warehouse.py b/stock/doctype/warehouse/test_warehouse.py
index 4e47d56..a4dadf3 100644
--- a/stock/doctype/warehouse/test_warehouse.py
+++ b/stock/doctype/warehouse/test_warehouse.py
@@ -5,16 +5,19 @@
[{
"doctype": "Warehouse",
"warehouse_name": "_Test Warehouse",
- "company": "_Test Company"
+ "company": "_Test Company",
+ "account": "_Test Account Stock In Hand - _TC"
}],
[{
"doctype": "Warehouse",
"warehouse_name": "_Test Warehouse 1",
- "company": "_Test Company"
+ "company": "_Test Company",
+ "account": "_Test Account Fixed Assets - _TC"
}],
[{
"doctype": "Warehouse",
"warehouse_name": "_Test Warehouse 2",
- "company": "_Test Company 1"
+ "company": "_Test Company 1",
+ "account": "_Test Account Stock In Hand - _TC"
}]
]
diff --git a/stock/doctype/warehouse/warehouse.js b/stock/doctype/warehouse/warehouse.js
index 9373c2a..3451863 100644
--- a/stock/doctype/warehouse/warehouse.js
+++ b/stock/doctype/warehouse/warehouse.js
@@ -15,4 +15,15 @@
if (check) {
return $c_obj(make_doclist(cdt, cdn), 'merge_warehouses', '', '');
}
-}
\ No newline at end of file
+}
+
+cur_frm.set_query("account", function() {
+ return {
+ filters: {
+ "company": cur_frm.doc.company,
+ "debit_or_credit": "Debit",
+ "is_pl_account": "No",
+ 'group_or_ledger': "Ledger"
+ }
+ }
+})
diff --git a/stock/doctype/warehouse/warehouse.txt b/stock/doctype/warehouse/warehouse.txt
index 631b968..1b1fc33 100644
--- a/stock/doctype/warehouse/warehouse.txt
+++ b/stock/doctype/warehouse/warehouse.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-03-07 18:50:32",
"docstatus": 0,
- "modified": "2013-07-23 12:01:16",
+ "modified": "2013-08-01 15:27:49",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -20,8 +20,7 @@
"name": "__common__",
"parent": "Warehouse",
"parentfield": "fields",
- "parenttype": "DocType",
- "read_only": 0
+ "parenttype": "DocType"
},
{
"doctype": "DocPerm",
@@ -29,9 +28,9 @@
"parent": "Warehouse",
"parentfield": "permissions",
"parenttype": "DocType",
- "permlevel": 0,
"read": 1,
- "report": 1
+ "report": 1,
+ "submit": 0
},
{
"doctype": "DocType",
@@ -43,7 +42,8 @@
"fieldtype": "Section Break",
"label": "Warehouse Detail",
"oldfieldtype": "Section Break",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -53,6 +53,7 @@
"oldfieldname": "warehouse_name",
"oldfieldtype": "Data",
"permlevel": 0,
+ "read_only": 0,
"reqd": 1
},
{
@@ -65,14 +66,25 @@
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
+ "read_only": 0,
"reqd": 1,
"search_index": 1
},
{
+ "description": "This account will be used for perpetual accounting for inventory e.g. Stock-in-Hand, Fixed Asset Account etc",
+ "doctype": "DocField",
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "label": "Account",
+ "options": "Account",
+ "permlevel": 0
+ },
+ {
"doctype": "DocField",
"fieldname": "column_break_4",
"fieldtype": "Section Break",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"description": "If set, data entry is only allowed for specified users. Else, entry is allowed for all users with requisite permissions.",
@@ -81,7 +93,8 @@
"fieldtype": "Table",
"label": "Warehouse Users",
"options": "Warehouse User",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"description": "For Reference Only.",
@@ -89,7 +102,8 @@
"fieldname": "warehouse_contact_info",
"fieldtype": "Section Break",
"label": "Warehouse Contact Info",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -100,7 +114,8 @@
"oldfieldname": "email_id",
"oldfieldtype": "Data",
"permlevel": 0,
- "print_hide": 0
+ "print_hide": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -110,7 +125,8 @@
"oldfieldname": "phone_no",
"oldfieldtype": "Int",
"options": "Phone",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -120,14 +136,16 @@
"oldfieldname": "mobile_no",
"oldfieldtype": "Int",
"options": "Phone",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
"fieldname": "column_break0",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -136,7 +154,8 @@
"label": "Address Line 1",
"oldfieldname": "address_line_1",
"oldfieldtype": "Data",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -145,7 +164,8 @@
"label": "Address Line 2",
"oldfieldname": "address_line_2",
"oldfieldtype": "Data",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -156,6 +176,7 @@
"oldfieldname": "city",
"oldfieldtype": "Data",
"permlevel": 0,
+ "read_only": 0,
"reqd": 0
},
{
@@ -166,7 +187,8 @@
"oldfieldname": "state",
"oldfieldtype": "Select",
"options": "Suggest",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -175,7 +197,8 @@
"label": "PIN",
"oldfieldname": "pin",
"oldfieldtype": "Int",
- "permlevel": 0
+ "permlevel": 0,
+ "read_only": 0
},
{
"description": "This feature is for merging duplicate warehouses. It will replace all the links of this warehouse by \"Merge Into\" warehouse. After merging you can delete this warehouse, as stock level for this warehouse will be zero.",
@@ -183,7 +206,8 @@
"fieldname": "merge_warehouses_section",
"fieldtype": "Section Break",
"label": "Merge Warehouses",
- "permlevel": 2
+ "permlevel": 2,
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -191,22 +215,32 @@
"fieldtype": "Link",
"label": "Merge Into",
"options": "Warehouse",
- "permlevel": 2
+ "permlevel": 2,
+ "read_only": 0
},
{
"doctype": "DocField",
"fieldname": "merge",
"fieldtype": "Button",
"label": "Merge",
- "permlevel": 2
+ "permlevel": 2,
+ "read_only": 0
},
{
"amend": 0,
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
+ "permlevel": 0,
"role": "Material Master Manager",
- "submit": 0,
+ "write": 1
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "doctype": "DocPerm",
+ "permlevel": 0,
+ "role": "System Manager",
"write": 1
},
{
@@ -214,20 +248,26 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "role": "Material User",
- "submit": 0,
+ "permlevel": 0,
+ "role": "Material Manager",
"write": 0
},
{
+ "amend": 0,
+ "cancel": 0,
+ "create": 0,
"doctype": "DocPerm",
- "role": "Sales User"
+ "permlevel": 0,
+ "role": "Material User",
+ "write": 0
},
{
+ "amend": 0,
+ "cancel": 0,
+ "create": 0,
"doctype": "DocPerm",
- "role": "Purchase User"
- },
- {
- "doctype": "DocPerm",
- "role": "Accounts User"
+ "permlevel": 2,
+ "role": "System Manager",
+ "write": 1
}
]
\ No newline at end of file
diff --git a/stock/stock_ledger.py b/stock/stock_ledger.py
index 4dcca67..fd46402 100644
--- a/stock/stock_ledger.py
+++ b/stock/stock_ledger.py
@@ -30,11 +30,11 @@
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 "[]")
- stock_value = 0.0
+ 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)
-
+
valuation_method = get_valuation_method(args["item_code"])
for sle in entries_to_fix:
diff --git a/stock/utils.py b/stock/utils.py
index d53d271..e4206c3 100644
--- a/stock/utils.py
+++ b/stock/utils.py
@@ -8,6 +8,36 @@
from webnotes.defaults import get_global_default
from webnotes.utils.email_lib import sendmail
+
+def get_stock_balance_on(warehouse_list, posting_date=None):
+ if not posting_date: posting_date = nowdate()
+
+ stock_ledger_entries = webnotes.conn.sql("""
+ SELECT
+ item_code, warehouse, stock_value
+ FROM
+ `tabStock Ledger Entry`
+ WHERE
+ warehouse in (%s)
+ AND posting_date <= %s
+ ORDER BY timestamp(posting_date, posting_time) DESC, name DESC
+ """ % (', '.join(['%s']*len(warehouse_list)), '%s'),
+ tuple(warehouse_list + [posting_date]), as_dict=1)
+
+ sle_map = {}
+ for sle in stock_ledger_entries:
+ sle_map.setdefault(sle.warehouse, {}).setdefault(sle.item_code, flt(sle.stock_value))
+
+ return sum([sum(item_dict.values()) for item_dict in sle_map.values()])
+
+def get_latest_stock_balance():
+ bin_map = {}
+ for d in webnotes.conn.sql("""SELECT item_code, warehouse, stock_value as stock_value
+ FROM tabBin""", as_dict=1):
+ bin_map.setdefault(d.warehouse, {}).setdefault(d.item_code, flt(d.stock_value))
+
+ return bin_map
+
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
if not end_of_life:
end_of_life = webnotes.conn.get_value("Item", item_code, "end_of_life")
@@ -178,7 +208,6 @@
sle.voucher_detail_no == item_row:
previous_stock_value = len(relevant_stock_ledger_entries) > i+1 and \
flt(relevant_stock_ledger_entries[i+1].stock_value) or 0.0
-
buying_amount = previous_stock_value - flt(sle.stock_value)
return buying_amount