[perpetual accounting] make gl entries relevant for different transactions

This commit is contained in:
Nabin Hait 2013-08-06 16:00:50 +05:30
parent 69f350a4d0
commit 43eba26dcf
29 changed files with 259 additions and 222 deletions

View File

@ -69,6 +69,7 @@ class DocType:
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",

View File

@ -33,7 +33,7 @@ class DocType:
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""",
@ -47,6 +47,6 @@ class DocType:
(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)

View File

@ -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",

View File

@ -235,29 +235,29 @@ class DocType(BuyingController):
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 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)
@ -340,9 +340,8 @@ class DocType(BuyingController):
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 = []
@ -379,18 +378,15 @@ class DocType(BuyingController):
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 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)) +
@ -398,7 +394,7 @@ class DocType(BuyingController):
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"
@ -417,7 +413,7 @@ class DocType(BuyingController):
})
)
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(
@ -444,6 +440,7 @@ class DocType(BuyingController):
)
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):

View File

@ -27,9 +27,9 @@ test_dependencies = ["Item", "Cost Center"]
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")
@ -54,9 +54,9 @@ class TestPurchaseInvoice(unittest.TestCase):
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")
@ -81,11 +81,11 @@ class TestPurchaseInvoice(unittest.TestCase):
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"
@ -112,7 +112,7 @@ class TestPurchaseInvoice(unittest.TestCase):
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])

View File

@ -332,7 +332,7 @@ cur_frm.set_query("income_account", "entries", function(doc) {
});
// 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: {

View File

@ -61,6 +61,8 @@ class DocType(SellingController):
self.validate_proj_cust()
self.validate_with_previous_doc()
self.validate_uom_is_integer("stock_uom", "qty")
self.validate_warehouse_with_company([d.warehouse
for d in self.doclist.get({"parentfield": "entries"})])
sales_com_obj = get_obj('Sales Common')
sales_com_obj.check_stop_sales_order(self)
@ -586,6 +588,10 @@ class DocType(SellingController):
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(
@ -627,7 +633,7 @@ class DocType(SellingController):
)
# 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"}):
@ -635,7 +641,7 @@ class DocType(SellingController):
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:

View File

@ -294,7 +294,7 @@ class TestSalesInvoice(unittest.TestCase):
"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()
@ -329,7 +329,7 @@ class TestSalesInvoice(unittest.TestCase):
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")
@ -389,11 +389,11 @@ class TestSalesInvoice(unittest.TestCase):
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
@ -417,10 +417,10 @@ class TestSalesInvoice(unittest.TestCase):
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"
@ -444,7 +444,7 @@ class TestSalesInvoice(unittest.TestCase):
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 \

View File

@ -34,7 +34,7 @@ class AccountsController(TransactionBase):
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):
@ -410,7 +410,6 @@ class AccountsController(TransactionBase):
def get_company_default(self, fieldname):
from accounts.utils import get_company_default
return get_company_default(self.doc.company, fieldname)
def get_stock_items(self):
stock_items = []

View File

@ -33,7 +33,7 @@ erpnext.stock.StockController = wn.ui.form.Controller.extend({
},
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,

View File

@ -72,18 +72,7 @@ cur_frm.fields_dict.receivables_group.get_query = function(doc) {
}
}
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": {
@ -108,10 +97,4 @@ if (sys_defaults.auto_inventory_accounting) {
}
}
}
cur_frm.fields_dict["stock_adjustment_cost_center"].get_query = function(doc) {
return {
"filters": {"company": doc.name}
}
}
}

View File

@ -2,7 +2,7 @@
{
"creation": "2013-04-10 08:35:39",
"docstatus": 0,
"modified": "2013-07-23 11:58: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",
@ -220,19 +227,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
},
{
@ -244,13 +241,6 @@
"options": "Account",
"read_only": 0
},
{
"doctype": "DocField",
"fieldname": "col_break23",
"fieldtype": "Column Break",
"read_only": 0,
"width": "50%"
},
{
"doctype": "DocField",
"fieldname": "stock_adjustment_account",
@ -269,15 +259,6 @@
"options": "Account",
"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",
@ -373,17 +354,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"
}
]

View File

@ -119,8 +119,8 @@ class DocType:
})
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"

View File

@ -46,8 +46,8 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
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) {
@ -200,7 +200,7 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
}
}
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];

View File

@ -107,6 +107,8 @@ class DocType(SellingController):
self.validate_for_items()
self.validate_warehouse()
self.validate_uom_is_integer("stock_uom", "qty")
self.validate_warehouse_with_company([d.warehouse
for d in self.doclist.get({"parentfield": "delivery_note_details"})])
sales_com_obj.validate_max_discount(self, 'delivery_note_details')
sales_com_obj.check_conversion_rate(self)
@ -174,7 +176,7 @@ class DocType(SellingController):
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'):
@ -332,32 +334,23 @@ class DocType(SellingController):
if not cint(webnotes.defaults.get_global_default("perpetual_accounting")):
return
gl_entries = []
warehouse_item_map = {}
gl_entries = []
warehouse_list = []
for item in self.doclist.get({"parentfield": "delivery_note_details"}):
self.check_expense_account(item)
warehouse_item_map.setdefault(item.warehouse, [])
if item.item_code not in warehouse_item_map[item.warehouse]:
warehouse_item_map[item.warehouse].append(item.item_code)
if [item.item_code, item.warehouse] not in item_warehouse:
item_warehouse.append([item.item_code, item.warehouse])
for
for wh, cc_dict in expense_account_map.items:
for cost_center, warehouse_list in cc_dict.items():
if item.buying_amount:
gl_entries += self.get_gl_entries_for_stock(item.expense_account,
cost_center=item.cost_center, warehouse_list=warehouse_list)
-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}"""

View File

@ -50,8 +50,8 @@ class TestDeliveryNote(unittest.TestCase):
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()
@ -69,8 +69,8 @@ class TestDeliveryNote(unittest.TestCase):
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()
@ -106,7 +106,7 @@ class TestDeliveryNote(unittest.TestCase):
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 = [
[

View File

@ -7,7 +7,7 @@ from webnotes.utils import flt
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

View File

@ -140,7 +140,10 @@ class DocType(BuyingController):
self.validate_inspection()
self.validate_uom_is_integer("uom", ["qty", "received_qty"])
self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_warehouse_with_company(reduce(lambda x,y: x+y,
[[d.warehouse, d.rejected_warehouse] for d in
self.doclist.get({"parentfield": "purchase_receipt_details"})]))
get_obj('Stock Ledger').validate_serial_no(self, 'purchase_receipt_details')
self.validate_challan_no()
@ -319,25 +322,21 @@ class DocType(BuyingController):
return
against_stock_account = self.get_company_default("stock_received_but_not_billed")
warehouse_list = [d.warehouse for d in
self.doclist.get({"parentfield": "purchase_receipt_details"})]
gl_entries = self.get_gl_entries_for_stock(against_stock_account, warehouse_list=warehouse_list)
gl_entries = []
warehouse_list = []
for d in self.doclist.get({"parentfield": "purchase_receipt_details"}):
if d.valuation_rate:
gl_entries += self.get_gl_entries_for_stock(against_stock_account,
d.valuation_rate, 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
stock_items = self.get_stock_items()
for item in self.doclist.get({"parentfield": "purchase_receipt_details"}):
if item.item_code in 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):

View File

@ -75,6 +75,8 @@ class DocType(StockController):
self.make_gl_entries()
def make_stock_ledger_entry(self, qty):
self.validate_warehouse_with_company([self.doc.warehouse])
sl_entries = [{
'item_code' : self.doc.item_code,
'warehouse' : self.doc.warehouse,
@ -102,7 +104,7 @@ class DocType(StockController):
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)):
@ -133,16 +135,24 @@ class DocType(StockController):
('\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)
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)

View File

@ -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"
},
@ -150,6 +150,14 @@
"reqd": 0,
"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",
@ -520,6 +528,13 @@
"read_only": 1,
"report_hide": 1
},
{
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
"role": "System Manager",
"write": 1
},
{
"cancel": 1,
"create": 1,

View File

@ -7,7 +7,6 @@ import webnotes, unittest
class TestSerialNo(unittest.TestCase):
def test_aii_gl_entries_for_serial_no_in_store(self):
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()
@ -96,7 +95,8 @@ test_records = [
"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"
}
]
]

View File

@ -25,9 +25,9 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
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";
@ -78,7 +78,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
}
};
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() {

View File

@ -51,6 +51,9 @@ class DocType(StockController):
self.validate_item()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "transfer_qty")
self.validate_warehouse_with_company(reduce(lambda x,y: x+y,
[[d.s_warehouse, d.t_warehouse] for d in
self.doclist.get({"parentfield": "mtn_details"})]))
self.validate_warehouse(pro_obj)
self.validate_production_order(pro_obj)
@ -184,32 +187,41 @@ class DocType(StockController):
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.con.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.s_warehouse
against_expense_account = t_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, 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'):

View File

@ -2,7 +2,7 @@
{
"creation": "2013-04-09 11:43:55",
"docstatus": 0,
"modified": "2013-07-05 14:56:06",
"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",
@ -209,6 +209,14 @@
"print_hide": 1,
"read_only": 0
},
{
"depends_on": "eval:sys_defaults.perpetual_accounting",
"doctype": "DocField",
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"doctype": "DocField",
"fieldname": "items_section",

View File

@ -7,7 +7,7 @@ from webnotes.utils import flt
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)
@ -45,7 +45,7 @@ class TestStockEntry(unittest.TestCase):
def test_material_receipt_gl_entry(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)
mr = webnotes.bean(copy=test_records[0])
mr.insert()
@ -80,7 +80,7 @@ class TestStockEntry(unittest.TestCase):
def test_material_issue_gl_entry(self):
self._clear_stock()
webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
webnotes.defaults.set_global_default("perpetual_accounting", 1)
mr = webnotes.bean(copy=test_records[0])
mr.insert()
@ -120,7 +120,7 @@ class TestStockEntry(unittest.TestCase):
def test_material_transfer_gl_entry(self):
self._clear_stock()
webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
webnotes.defaults.set_global_default("perpetual_accounting", 1)
mr = webnotes.bean(copy=test_records[0])
mr.insert()

View File

@ -25,7 +25,7 @@ erpnext.stock.StockReconciliation = erpnext.stock.StockController.extend({
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: {
@ -41,7 +41,7 @@ erpnext.stock.StockReconciliation = erpnext.stock.StockController.extend({
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() {

View File

@ -69,8 +69,10 @@ class DocType(StockController):
if len(rows) > 100:
msgprint(_("""Sorry! We can only allow upto 100 rows for Stock Reconciliation."""),
raise_exception=True)
warehouse_list = []
for row_num, row in enumerate(rows):
if row[1] not in warehouse_list:
warehouse_list.append(row[1])
# find duplicates
if [row[0], row[1]] in item_warehouse_combinations:
self.validation_messages.append(_get_msg(row_num, "Duplicate entry"))
@ -102,6 +104,8 @@ class DocType(StockController):
raise webnotes.ValidationError
self.validate_warehouse_with_company(warehouse_list)
def validate_item(self, item_code, row_num):
from stock.utils import validate_end_of_life, validate_is_stock_item, \
validate_cancelled_item
@ -301,26 +305,54 @@ class DocType(StockController):
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, self.doc.cost_center)
if warehouse not in warehouse_list:
warehouse_list.append(warehouse)
if gl_entries:
from accounts.general_ledger import make_gl_entries
make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2)
self.sync_stock_account_balance(warehouse_list, self.doc.cost_center)
def validate_expense_account(self):
if not 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():

View File

@ -2,7 +2,7 @@
{
"creation": "2013-03-28 10:35:31",
"docstatus": 0,
"modified": "2013-07-22 15:22:44",
"modified": "2013-08-05 17:18:14",
"modified_by": "Administrator",
"owner": "Administrator"
},
@ -31,7 +31,6 @@
"permlevel": 0
},
{
"amend": 0,
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
@ -42,7 +41,6 @@
"permlevel": 0,
"read": 1,
"report": 1,
"role": "Material Manager",
"submit": 1,
"write": 1
},
@ -102,13 +100,21 @@
"reqd": 1
},
{
"depends_on": "eval:sys_defaults.auto_inventory_accounting",
"depends_on": "eval:sys_defaults.perpetual_accounting",
"doctype": "DocField",
"fieldname": "expense_account",
"fieldtype": "Link",
"label": "Expense Account",
"options": "Account"
},
{
"depends_on": "eval:sys_defaults.perpetual_accounting",
"doctype": "DocField",
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"doctype": "DocField",
"fieldname": "col1",
@ -151,13 +157,19 @@
{
"doctype": "DocField",
"fieldname": "stock_value_difference",
"fieldtype": "Currency",
"fieldtype": "Long Text",
"hidden": 1,
"in_list_view": 1,
"in_list_view": 0,
"label": "Stock Value Difference",
"print_hide": 1
},
{
"doctype": "DocPerm"
"amend": 0,
"doctype": "DocPerm",
"role": "Material Manager"
},
{
"doctype": "DocPerm",
"role": "System Manager"
}
]

View File

@ -9,7 +9,7 @@ from accounts.utils import get_fiscal_year
class TestStockReconciliation(unittest.TestCase):
def test_reco_for_fifo(self):
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
webnotes.defaults.set_global_default("perpetual_accounting", 0)
# [[qty, valuation_rate, posting_date,
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
input_data = [
@ -53,7 +53,7 @@ class TestStockReconciliation(unittest.TestCase):
def test_reco_for_moving_average(self):
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
webnotes.defaults.set_global_default("perpetual_accounting", 0)
# [[qty, valuation_rate, posting_date,
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
input_data = [
@ -99,7 +99,7 @@ class TestStockReconciliation(unittest.TestCase):
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]]
@ -131,10 +131,10 @@ class TestStockReconciliation(unittest.TestCase):
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 test_reco_moving_average_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]]
@ -166,7 +166,7 @@ class TestStockReconciliation(unittest.TestCase):
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):