aii: stock reconciliation with test case

This commit is contained in:
Nabin Hait 2013-03-19 12:01:46 +05:30
parent c3afb256b4
commit 89a94d8135
17 changed files with 383 additions and 222 deletions

View File

@ -207,4 +207,4 @@ def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
where group_or_ledger = 'Group' and docstatus != 2 and company = %s where group_or_ledger = 'Group' and docstatus != 2 and company = %s
and %s like %s order by name limit %s, %s""" % and %s like %s order by name limit %s, %s""" %
("%s", searchfield, "%s", "%s", "%s"), ("%s", searchfield, "%s", "%s", "%s"),
(filters["company"], "%%%s%%" % txt, start, page_len), as_list=1) (filters["company"], "%%%s%%" % txt, start, page_len), as_list=1)

View File

@ -1,8 +1,8 @@
[ [
{ {
"creation": "2013-01-10 16:34:06", "creation": "2013-01-22 16:50:25",
"docstatus": 0, "docstatus": 0,
"modified": "2013-01-22 14:46:59", "modified": "2013-03-13 12:29:40",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -23,7 +23,6 @@
"permlevel": 0 "permlevel": 0
}, },
{ {
"amend": 0,
"cancel": 1, "cancel": 1,
"create": 1, "create": 1,
"doctype": "DocPerm", "doctype": "DocPerm",
@ -42,22 +41,6 @@
"doctype": "DocType", "doctype": "DocType",
"name": "Fiscal Year" "name": "Fiscal Year"
}, },
{
"doctype": "DocField",
"fieldname": "year_details",
"fieldtype": "Section Break",
"label": "Fiscal Year Details",
"oldfieldtype": "Section Break"
},
{
"doctype": "DocField",
"fieldname": "trash_reason",
"fieldtype": "Small Text",
"label": "Trash Reason",
"oldfieldname": "trash_reason",
"oldfieldtype": "Small Text",
"read_only": 1
},
{ {
"description": "For e.g. 2012, 2012-13", "description": "For e.g. 2012, 2012-13",
"doctype": "DocField", "doctype": "DocField",
@ -73,6 +56,7 @@
"fieldname": "year_start_date", "fieldname": "year_start_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Year Start Date", "label": "Year Start Date",
"no_copy": 1,
"oldfieldname": "year_start_date", "oldfieldname": "year_start_date",
"oldfieldtype": "Date", "oldfieldtype": "Date",
"reqd": 1 "reqd": 1
@ -84,6 +68,7 @@
"fieldname": "is_fiscal_year_closed", "fieldname": "is_fiscal_year_closed",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Year Closed", "label": "Year Closed",
"no_copy": 1,
"oldfieldname": "is_fiscal_year_closed", "oldfieldname": "is_fiscal_year_closed",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nNo\nYes", "options": "\nNo\nYes",

View File

@ -238,8 +238,11 @@ cur_frm.cscript.expense_head = function(doc, cdt, cdn){
refresh_field('entries'); refresh_field('entries');
} }
cur_frm.fields_dict['entries'].grid.get_field("cost_center").get_query = function(doc) { cur_frm.fields_dict["entries"].grid.get_field("cost_center").get_query = function(doc) {
return 'SELECT `tabCost Center`.`name` FROM `tabCost Center` WHERE `tabCost Center`.`company_name` = "' +doc.company+'" AND `tabCost Center`.%(key)s LIKE "%s" AND `tabCost Center`.`group_or_ledger` = "Ledger" AND `tabCost Center`.docstatus != 2 ORDER BY `tabCost Center`.`name` ASC LIMIT 50'; return {
query: "accounts.utils.get_cost_center_list",
filters: { company_name: doc.company}
}
} }
cur_frm.cscript.cost_center = function(doc, cdt, cdn){ cur_frm.cscript.cost_center = function(doc, cdt, cdn){

View File

@ -271,8 +271,6 @@ cur_frm.cscript.is_opening = function(doc, dt, dn) {
if (doc.is_opening == 'Yes') unhide_field('aging_date'); if (doc.is_opening == 'Yes') unhide_field('aging_date');
} }
/* **************************** TRIGGERS ********************************** */
// Get Items based on SO or DN Selected // Get Items based on SO or DN Selected
cur_frm.cscript.get_items = function(doc, dt, dn) { cur_frm.cscript.get_items = function(doc, dt, dn) {
var callback = function(r,rt) { var callback = function(r,rt) {
@ -371,6 +369,18 @@ cur_frm.set_query("income_account", "entries", function(doc) {
return 'SELECT tabAccount.name FROM tabAccount WHERE (tabAccount.debit_or_credit="Credit" OR tabAccount.account_type = "Income Account") AND tabAccount.group_or_ledger="Ledger" AND tabAccount.docstatus!=2 AND tabAccount.company="'+doc.company+'" AND tabAccount.%(key)s LIKE "%s"'; return 'SELECT tabAccount.name FROM tabAccount WHERE (tabAccount.debit_or_credit="Credit" OR tabAccount.account_type = "Income Account") AND tabAccount.group_or_ledger="Ledger" AND tabAccount.docstatus!=2 AND tabAccount.company="'+doc.company+'" AND tabAccount.%(key)s LIKE "%s"';
}) })
// expense account
cur_frm.fields_dict['entries'].grid.get_field('expense_account').get_query = function(doc) {
return {
"query": "accounts.utils.get_account_list",
"filters": {
"is_pl_account": "Yes",
"debit_or_credit": "Debit",
"company": doc.company
}
}
}
// warehouse in detail table // warehouse in detail table
//---------------------------- //----------------------------
cur_frm.fields_dict['entries'].grid.get_field('warehouse').get_query= function(doc, cdt, cdn) { cur_frm.fields_dict['entries'].grid.get_field('warehouse').get_query= function(doc, cdt, cdn) {
@ -380,8 +390,11 @@ cur_frm.fields_dict['entries'].grid.get_field('warehouse').get_query= function(d
// Cost Center in Details Table // Cost Center in Details Table
// ----------------------------- // -----------------------------
cur_frm.fields_dict.entries.grid.get_field("cost_center").get_query = function(doc) { cur_frm.fields_dict["entries"].grid.get_field("cost_center").get_query = function(doc) {
return 'SELECT `tabCost Center`.`name` FROM `tabCost Center` WHERE `tabCost Center`.`company_name` = "' +doc.company+'" AND `tabCost Center`.%(key)s LIKE "%s" AND `tabCost Center`.`group_or_ledger` = "Ledger" AND `tabCost Center`.`docstatus`!= 2 ORDER BY `tabCost Center`.`name` ASC LIMIT 50'; return {
query: "accounts.utils.get_cost_center_list",
filters: { company_name: doc.company}
}
} }
// Sales Order // Sales Order

View File

@ -804,8 +804,9 @@ class DocType(SellingController):
item_buying_amount = 0 item_buying_amount = 0
if stock_ledger_entries: if stock_ledger_entries:
# is pos and update stock # is pos and update stock
item_buying_amount = get_buying_amount(item.item_code, item.warehouse, item.qty, item_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) self.doc.doctype, self.doc.name, item.name, stock_ledger_entries, item_sales_bom)
item.buying_amount = item_buying_amount > 0 and item_buying_amount or 0
elif item.delivery_note and item.dn_detail: elif item.delivery_note and item.dn_detail:
# against delivery note # against delivery note
dn_item = webnotes.conn.get_value("Delivery Note Item", item.dn_detail, dn_item = webnotes.conn.get_value("Delivery Note Item", item.dn_detail,

View File

@ -2,7 +2,7 @@
{ {
"creation": "2013-03-07 11:42:55", "creation": "2013-03-07 11:42:55",
"docstatus": 0, "docstatus": 0,
"modified": "2013-03-11 14:58:50", "modified": "2013-03-18 15:41:19",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -207,14 +207,16 @@
"width": "120px" "width": "120px"
}, },
{ {
"depends_on": "eval:sys_defaults.auto_inventory_accounting",
"doctype": "DocField", "doctype": "DocField",
"fieldname": "expense_account", "fieldname": "expense_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 0,
"in_filter": 1, "in_filter": 1,
"label": "Expense Account", "label": "Expense Account",
"options": "Account", "options": "Account",
"print_hide": 1 "print_hide": 1,
"width": "120px"
}, },
{ {
"doctype": "DocField", "doctype": "DocField",

View File

@ -24,8 +24,10 @@ def execute(filters=None):
data = [] data = []
for row in delivery_note_items: for row in delivery_note_items:
selling_amount = flt(row.amount) selling_amount = flt(row.amount)
buying_amount = get_buying_amount(row.item_code, row.warehouse, buying_amount = get_buying_amount(row.item_code, row.warehouse, -1*row.qty,
row.qty, "Delivery Note", row.name, row.item_row, stock_ledger_entries, item_sales_bom) "Delivery Note", row.name, row.item_row, stock_ledger_entries, item_sales_bom)
buying_amount = buying_amount > 0 and buying_amount or 0
if selling_amount: if selling_amount:
gross_profit = selling_amount - buying_amount gross_profit = selling_amount - buying_amount
gross_profit_percent = (gross_profit / selling_amount) * 100.0 gross_profit_percent = (gross_profit / selling_amount) * 100.0

View File

@ -18,6 +18,7 @@ from __future__ import unicode_literals
import webnotes import webnotes
from webnotes import msgprint, _ from webnotes import msgprint, _
from webnotes.utils import flt from webnotes.utils import flt
from utilities.transaction_base import TransactionBase from utilities.transaction_base import TransactionBase
class AccountsController(TransactionBase): class AccountsController(TransactionBase):
@ -70,7 +71,6 @@ class AccountsController(TransactionBase):
def get_stock_in_hand_account(self): def get_stock_in_hand_account(self):
stock_in_hand_account = webnotes.conn.get_value("Company", self.doc.company, "stock_in_hand_account") stock_in_hand_account = webnotes.conn.get_value("Company", self.doc.company, "stock_in_hand_account")
if not stock_in_hand_account: if not stock_in_hand_account:
msgprint(_("Missing") + ": " msgprint(_("Missing") + ": "
+ _(webnotes.get_doctype("company").get_label("stock_in_hand_account") + _(webnotes.get_doctype("company").get_label("stock_in_hand_account")

View File

@ -24,9 +24,9 @@ from buying.utils import get_item_details
from setup.utils import get_company_currency from setup.utils import get_company_currency
from webnotes.model.utils import round_floats_in_doc from webnotes.model.utils import round_floats_in_doc
from controllers.accounts_controller import AccountsController from controllers.stock_controller import StockController
class BuyingController(AccountsController): class BuyingController(StockController):
def validate(self): def validate(self):
if self.meta.get_field("currency"): if self.meta.get_field("currency"):
self.company_currency = get_company_currency(self.doc.company) self.company_currency = get_company_currency(self.doc.company)

View File

@ -17,7 +17,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import cstr, set_default from webnotes.utils import cstr
from webnotes.model.doc import Document from webnotes.model.doc import Document
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
import webnotes.defaults import webnotes.defaults
@ -49,6 +49,8 @@ class DocType:
['Earnest Money','Securities and Deposits','Ledger','No','','Debit',self.doc.name,''], ['Earnest Money','Securities and Deposits','Ledger','No','','Debit',self.doc.name,''],
['Stock In Hand','Current Assets','Group','No','','Debit',self.doc.name,''], ['Stock In Hand','Current Assets','Group','No','','Debit',self.doc.name,''],
['Stock','Stock In Hand','Ledger','No','','Debit',self.doc.name,''], ['Stock','Stock In Hand','Ledger','No','','Debit',self.doc.name,''],
['Stock Delivered But Not Billed', 'Stock In Hand', 'Ledger',
'No', '', 'Debit', self.doc.name, ''],
['Tax Assets','Current Assets','Group','No','','Debit',self.doc.name,''], ['Tax Assets','Current Assets','Group','No','','Debit',self.doc.name,''],
['Stock Delivered But Not Billed','Current Assets','Ledger','No','','Debit',self.doc.name,''], ['Stock Delivered But Not Billed','Current Assets','Ledger','No','','Debit',self.doc.name,''],
['Fixed Assets','Application of Funds (Assets)','Group','No','','Debit',self.doc.name,''], ['Fixed Assets','Application of Funds (Assets)','Group','No','','Debit',self.doc.name,''],
@ -62,9 +64,9 @@ class DocType:
['Temporary Account (Assets)','Temporary Accounts (Assets)','Ledger','No','','Debit',self.doc.name,''], ['Temporary Account (Assets)','Temporary Accounts (Assets)','Ledger','No','','Debit',self.doc.name,''],
['Expenses','','Group','Yes','Expense Account','Debit',self.doc.name,''], ['Expenses','','Group','Yes','Expense Account','Debit',self.doc.name,''],
['Direct Expenses','Expenses','Group','Yes','Expense Account','Debit',self.doc.name,''], ['Direct Expenses','Expenses','Group','Yes','Expense Account','Debit',self.doc.name,''],
['Cost of Goods Sold','Direct Expenses','Ledger','Yes','Expense Account','Debit',self.doc.name,''], ['Stock Expenses','Direct Expenses','Group','Yes','Expense Account','Debit',self.doc.name,''],
['Expenses Included In Valuation','Direct Expenses','Ledger','Yes','Expense Account','Debit',self.doc.name,''], ['Cost of Goods Sold','Stock Expenses','Ledger','Yes','Expense Account','Debit',self.doc.name,''],
['Stock Adjustment','Direct Expenses','Ledger','Yes','Expense Account','Debit',self.doc.name,''], ['Stock Adjustment','Stock Expenses','Ledger','Yes','Expense Account','Debit',self.doc.name,''],
['Indirect Expenses','Expenses','Group','Yes','Expense Account','Debit',self.doc.name,''], ['Indirect Expenses','Expenses','Group','Yes','Expense Account','Debit',self.doc.name,''],
['Advertising and Publicity','Indirect Expenses','Ledger','Yes','Chargeable','Debit',self.doc.name,''], ['Advertising and Publicity','Indirect Expenses','Ledger','Yes','Chargeable','Debit',self.doc.name,''],
['Bad Debts Written Off','Indirect Expenses','Ledger','Yes','Expense Account','Debit',self.doc.name,''], ['Bad Debts Written Off','Indirect Expenses','Ledger','Yes','Expense Account','Debit',self.doc.name,''],
@ -101,6 +103,9 @@ class DocType:
['Shareholders Funds','Capital Account','Group','No','','Credit',self.doc.name,''], ['Shareholders Funds','Capital Account','Group','No','','Credit',self.doc.name,''],
['Current Liabilities','Source of Funds (Liabilities)','Group','No','','Credit',self.doc.name,''], ['Current Liabilities','Source of Funds (Liabilities)','Group','No','','Credit',self.doc.name,''],
['Accounts Payable','Current Liabilities','Group','No','','Credit',self.doc.name,''], ['Accounts Payable','Current Liabilities','Group','No','','Credit',self.doc.name,''],
['Stock Liabilities','Current Liabilities','Group','No','','Credit',self.doc.name,''],
['Stock Received But Not Billed', 'Stock Liabilities', 'Ledger',
'No', '', 'Credit', self.doc.name, ''],
['Duties and Taxes','Current Liabilities','Group','No','','Credit',self.doc.name,''], ['Duties and Taxes','Current Liabilities','Group','No','','Credit',self.doc.name,''],
['Loans (Liabilities)','Current Liabilities','Group','No','','Credit',self.doc.name,''], ['Loans (Liabilities)','Current Liabilities','Group','No','','Credit',self.doc.name,''],
['Secured Loans','Loans (Liabilities)','Group','No','','Credit',self.doc.name,''], ['Secured Loans','Loans (Liabilities)','Group','No','','Credit',self.doc.name,''],
@ -186,14 +191,30 @@ class DocType:
self.doc.letter_head = header self.doc.letter_head = header
# Set default AR and AP group def set_default_accounts(self):
# --------------------------------------------------- if not self.doc.receivables_group and webnotes.conn.exists('Account',
def set_default_groups(self): 'Accounts Receivable - ' + self.doc.abbr):
if not self.doc.receivables_group: webnotes.conn.set(self.doc, 'receivables_group', 'Accounts Receivable - ' +
webnotes.conn.set(self.doc, 'receivables_group', 'Accounts Receivable - '+self.doc.abbr) self.doc.abbr)
if not self.doc.payables_group:
webnotes.conn.set(self.doc, 'payables_group', 'Accounts Payable - '+self.doc.abbr) if not self.doc.payables_group and webnotes.conn.exists('Account',
'Accounts Payable - ' + self.doc.abbr):
webnotes.conn.set(self.doc, 'payables_group', 'Accounts Payable - ' + self.doc.abbr)
if not self.doc.stock_delivered_but_not_billed and webnotes.conn.exists("Account",
"Stock Delivered But Not Billed - " + self.doc.abbr):
webnotes.conn.set(self.doc, "stock_delivered_but_not_billed",
"Stock Delivered But Not Billed - " + self.doc.abbr)
if not self.doc.stock_received_but_not_billed and webnotes.conn.exists("Account",
"Stock Received But Not Billed - " + self.doc.abbr):
webnotes.conn.set(self.doc, "stock_received_but_not_billed",
"Stock Received But Not Billed - " + self.doc.abbr)
if not self.doc.stock_adjustment_account and webnotes.conn.exists("Account",
"Stock Adjustment - " + self.doc.abbr):
webnotes.conn.set(self.doc, "stock_adjustment_account", "Stock Adjustment - " +
self.doc.abbr)
# Create default cost center # Create default cost center
# --------------------------------------------------- # ---------------------------------------------------
@ -228,7 +249,7 @@ class DocType:
self.doc.name) self.doc.name)
if not ac: if not ac:
self.create_default_accounts() self.create_default_accounts()
self.set_default_groups() self.set_default_accounts()
cc = sql("select name from `tabCost Center` where cost_center_name = 'Root' and company_name = '%s'"%(self.doc.name)) cc = sql("select name from `tabCost Center` where cost_center_name = 'Root' and company_name = '%s'"%(self.doc.name))
if not cc: if not cc:
self.create_default_cost_center() self.create_default_cost_center()
@ -258,9 +279,6 @@ class DocType:
#update value as blank for tabSingles Global Defaults #update value as blank for tabSingles Global Defaults
sql("update `tabSingles` set value = '' where doctype='Global Defaults' and field = 'default_company' and value = %s", self.doc.name) sql("update `tabSingles` set value = '' where doctype='Global Defaults' and field = 'default_company' and value = %s", self.doc.name)
# on rename
# ---------
def on_rename(self,newdn,olddn): def on_rename(self,newdn,olddn):
sql("update `tabCompany` set company_name = '%s' where name = '%s'" %(newdn,olddn)) sql("update `tabCompany` set company_name = '%s' where name = '%s'" %(newdn,olddn))
sql("update `tabSingles` set value = %s where doctype='Global Defaults' and field = 'default_company' and value = %s", (newdn, olddn)) sql("update `tabSingles` set value = %s where doctype='Global Defaults' and field = 'default_company' and value = %s", (newdn, olddn))

View File

@ -1,8 +1,8 @@
[ [
{ {
"creation": "2013-02-22 01:27:54", "creation": "2013-02-27 09:38:05",
"docstatus": 0, "docstatus": 0,
"modified": "2013-02-26 10:57:39", "modified": "2013-03-18 16:34:04",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -124,17 +124,6 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Account" "options": "Account"
}, },
{
"depends_on": "eval:!doc.__islocal",
"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",
"read_only": 0
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "column_break0", "fieldname": "column_break0",
@ -181,6 +170,50 @@
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nWarn\nIgnore\nStop" "options": "\nWarn\nIgnore\nStop"
}, },
{
"depends_on": "eval:!doc.__islocal && sys_defaults.auto_inventory_accounting",
"doctype": "DocField",
"fieldname": "auto_inventory_accounting_settings",
"fieldtype": "Section Break",
"label": "Auto Inventory Accounting Settings"
},
{
"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",
"read_only": 0
},
{
"doctype": "DocField",
"fieldname": "stock_adjustment_account",
"fieldtype": "Link",
"label": "Stock Adjustment Account",
"options": "Account"
},
{
"doctype": "DocField",
"fieldname": "col_break23",
"fieldtype": "Column Break",
"width": "50%"
},
{
"doctype": "DocField",
"fieldname": "stock_delivered_but_not_billed",
"fieldtype": "Link",
"label": "Stock Delivered But Not Billed",
"options": "Account"
},
{
"doctype": "DocField",
"fieldname": "stock_received_but_not_billed",
"fieldtype": "Link",
"label": "Stock Received But Not Billed",
"options": "Account"
},
{ {
"description": "For reference only.", "description": "For reference only.",
"doctype": "DocField", "doctype": "DocField",

View File

@ -24,7 +24,6 @@ from webnotes import msgprint
sql = webnotes.conn.sql sql = webnotes.conn.sql
from controllers.selling_controller import SellingController from controllers.selling_controller import SellingController
class DocType(SellingController): class DocType(SellingController):
@ -401,9 +400,10 @@ class DocType(SellingController):
if stock_ledger_entries: if stock_ledger_entries:
for item in self.doclist.get({"parentfield": "delivery_note_details"}): for item in self.doclist.get({"parentfield": "delivery_note_details"}):
item.buying_amount = get_buying_amount(item.item_code, item.warehouse, item.qty, buying_amount = get_buying_amount(item.item_code, item.warehouse, -1*item.qty,
self.doc.doctype, self.doc.name, item.name, stock_ledger_entries, self.doc.doctype, self.doc.name, item.name, stock_ledger_entries,
item_sales_bom) item_sales_bom)
item.buying_amount = buying_amount > 0 and buying_amount or 0
webnotes.conn.set_value("Delivery Note Item", item.name, "buying_amount", webnotes.conn.set_value("Delivery Note Item", item.name, "buying_amount",
item.buying_amount) item.buying_amount)
@ -420,32 +420,11 @@ class DocType(SellingController):
if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")): if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")):
return return
abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr") against_stock_account = "Stock Delivered But Not Billed - %s" % (self.company_abbr,)
stock_delivered_account = "Stock Delivered But Not Billed - %s" % (abbr,)
stock_in_hand_account = self.get_stock_in_hand_account()
total_buying_amount = self.get_total_buying_amount() total_buying_amount = self.get_total_buying_amount()
if total_buying_amount:
gl_entries = [ super(DocType, self).make_gl_entries(against_stock_account, -1*total_buying_amount)
# credit stock in hand account
self.get_gl_dict({
"account": stock_in_hand_account,
"against": stock_delivered_account,
"credit": total_buying_amount,
"remarks": self.doc.remarks or "Accounting Entry for Stock",
}, self.doc.docstatus == 2),
# debit stock received but not billed account
self.get_gl_dict({
"account": stock_delivered_account,
"against": stock_in_hand_account,
"debit": total_buying_amount,
"remarks": self.doc.remarks or "Accounting Entry for Stock",
}, self.doc.docstatus == 2),
]
from accounts.general_ledger import make_gl_entries
make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2)
def get_total_buying_amount(self): def get_total_buying_amount(self):
total_buying_amount = sum([item.buying_amount for item in total_buying_amount = sum([item.buying_amount for item in
self.doclist.get({"parentfield": "delivery_note_details"})]) self.doclist.get({"parentfield": "delivery_note_details"})])

View File

@ -20,8 +20,7 @@ import webnotes
from webnotes.utils import cstr, flt, cint from webnotes.utils import cstr, flt, cint
from webnotes.model.bean import getlist from webnotes.model.bean import getlist
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
from webnotes.model.doc import Document from webnotes import msgprint
from webnotes import msgprint, _
sql = webnotes.conn.sql sql = webnotes.conn.sql
@ -319,32 +318,10 @@ class DocType(BuyingController):
if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")): if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")):
return return
abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr") against_stock_account = "Stock Received But Not Billed - %s" % (self.company_abbr,)
stock_received_account = "Stock Received But Not Billed - %s" % (abbr,)
stock_in_hand_account = self.get_stock_in_hand_account()
total_valuation_amount = self.get_total_valuation_amount() total_valuation_amount = self.get_total_valuation_amount()
if total_valuation_amount: super(DocType, self).make_gl_entries(against_stock_account, total_valuation_amount)
gl_entries = [
# debit stock in hand account
self.get_gl_dict({
"account": stock_in_hand_account,
"against": stock_received_account,
"debit": total_valuation_amount,
"remarks": self.doc.remarks or "Accounting Entry for Stock",
}, self.doc.docstatus == 2),
# credit stock received but not billed account
self.get_gl_dict({
"account": stock_received_account,
"against": stock_in_hand_account,
"credit": total_valuation_amount,
"remarks": self.doc.remarks or "Accounting Entry for Stock",
}, self.doc.docstatus == 2),
]
from accounts.general_ledger import make_gl_entries
make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2)
def get_total_valuation_amount(self): def get_total_valuation_amount(self):
total_valuation_amount = 0.0 total_valuation_amount = 0.0

View File

@ -18,6 +18,10 @@ wn.require("public/app/js/controllers/stock_controller.js");
wn.provide("erpnext.stock"); wn.provide("erpnext.stock");
erpnext.stock.StockReconciliation = erpnext.stock.StockController.extend({ erpnext.stock.StockReconciliation = erpnext.stock.StockController.extend({
setup: function() {
this.frm.add_fetch("company", "stock_adjustment_account", "expense_account");
},
refresh: function() { refresh: function() {
if(this.frm.doc.docstatus===0) { if(this.frm.doc.docstatus===0) {
this.show_download_template(); this.show_download_template();
@ -122,4 +126,15 @@ erpnext.stock.StockReconciliation = erpnext.stock.StockController.extend({
}, },
}); });
cur_frm.cscript = new erpnext.stock.StockReconciliation({frm: cur_frm}); cur_frm.cscript = new erpnext.stock.StockReconciliation({frm: cur_frm});
cur_frm.fields_dict["expense_account"].get_query = function(doc) {
return {
"query": "accounts.utils.get_account_list",
"filters": {
"is_pl_account": "Yes",
"debit_or_credit": "Debit",
"company": doc.company
}
}
}

View File

@ -18,22 +18,26 @@ from __future__ import unicode_literals
import webnotes import webnotes
import json import json
from webnotes import msgprint, _ from webnotes import msgprint, _
from webnotes.utils import cstr, flt from webnotes.utils import cstr, flt, cint
from webnotes.model.controller import DocListController
from stock.stock_ledger import update_entries_after from stock.stock_ledger import update_entries_after
from controllers.stock_controller import StockController
class DocType(DocListController): class DocType(StockController):
def setup(self): def setup(self):
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"] self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
self.entries = []
def validate(self): def validate(self):
self.validate_data() self.validate_data()
def on_submit(self): def on_submit(self):
self.insert_stock_ledger_entries() self.insert_stock_ledger_entries()
self.set_stock_value_difference()
self.make_gl_entries()
def on_cancel(self): def on_cancel(self):
self.delete_stock_ledger_entries() self.delete_stock_ledger_entries()
self.make_gl_entries()
def validate_data(self): def validate_data(self):
if not self.doc.reconciliation_json: if not self.doc.reconciliation_json:
@ -134,6 +138,7 @@ class DocType(DocListController):
data = json.loads(self.doc.reconciliation_json) data = json.loads(self.doc.reconciliation_json)
for row_num, row in enumerate(data[data.index(self.head_row)+1:]): for row_num, row in enumerate(data[data.index(self.head_row)+1:]):
row = webnotes._dict(zip(row_template, row)) row = webnotes._dict(zip(row_template, row))
row["row_num"] = row_num
previous_sle = get_previous_sle({ previous_sle = get_previous_sle({
"item_code": row.item_code, "item_code": row.item_code,
"warehouse": row.warehouse, "warehouse": row.warehouse,
@ -162,8 +167,7 @@ class DocType(DocListController):
def sle_for_moving_avg(self, row, previous_sle, change_in_qty, change_in_rate): def sle_for_moving_avg(self, row, previous_sle, change_in_qty, change_in_rate):
"""Insert Stock Ledger Entries for Moving Average valuation""" """Insert Stock Ledger Entries for Moving Average valuation"""
def _get_incoming_rate(qty, valuation_rate, previous_qty, def _get_incoming_rate(qty, valuation_rate, previous_qty, previous_valuation_rate):
previous_valuation_rate):
if previous_valuation_rate == 0: if previous_valuation_rate == 0:
return flt(valuation_rate) return flt(valuation_rate)
else: else:
@ -177,9 +181,9 @@ class DocType(DocListController):
incoming_rate = _get_incoming_rate(flt(row.qty), flt(row.valuation_rate), incoming_rate = _get_incoming_rate(flt(row.qty), flt(row.valuation_rate),
flt(previous_sle.get("qty_after_transaction")), flt(previous_sle.get("qty_after_transaction")),
flt(previous_sle.get("valuation_rate"))) flt(previous_sle.get("valuation_rate")))
self.insert_entries({"actual_qty": change_in_qty, row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
"incoming_rate": incoming_rate}, row) self.insert_entries({"actual_qty": change_in_qty, "incoming_rate": incoming_rate}, row)
elif change_in_rate and flt(previous_sle.get("qty_after_transaction")) > 0: elif change_in_rate and flt(previous_sle.get("qty_after_transaction")) > 0:
# if no change in qty, but change in rate # if no change in qty, but change in rate
@ -190,9 +194,11 @@ class DocType(DocListController):
flt(previous_sle.get("valuation_rate"))) flt(previous_sle.get("valuation_rate")))
# +1 entry # +1 entry
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment +1"
self.insert_entries({"actual_qty": 1, "incoming_rate": incoming_rate}, row) self.insert_entries({"actual_qty": 1, "incoming_rate": incoming_rate}, row)
# -1 entry # -1 entry
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment -1"
self.insert_entries({"actual_qty": -1}, row) self.insert_entries({"actual_qty": -1}, row)
def sle_for_fifo(self, row, previous_sle, change_in_qty, change_in_rate): def sle_for_fifo(self, row, previous_sle, change_in_qty, change_in_rate):
@ -206,14 +212,16 @@ class DocType(DocListController):
if previous_stock_queue != [[row.qty, row.valuation_rate]]: if previous_stock_queue != [[row.qty, row.valuation_rate]]:
# make entry as per attachment # make entry as per attachment
if row.qty: if row.qty:
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
self.insert_entries({"actual_qty": row.qty, self.insert_entries({"actual_qty": row.qty,
"incoming_rate": flt(row.valuation_rate)}, row) "incoming_rate": flt(row.valuation_rate)}, row)
# Make reverse entry # Make reverse entry
if previous_stock_qty: if previous_stock_qty:
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Reverse Entry"
self.insert_entries({"actual_qty": -1 * previous_stock_qty, self.insert_entries({"actual_qty": -1 * previous_stock_qty,
"incoming_rate": previous_stock_qty < 0 and \ "incoming_rate": previous_stock_qty < 0 and
flt(row.valuation_rate) or 0}, row) flt(row.valuation_rate) or 0}, row)
if change_in_qty: if change_in_qty:
@ -221,8 +229,7 @@ class DocType(DocListController):
# dont want change in valuation # dont want change in valuation
if previous_stock_qty > 0: if previous_stock_qty > 0:
# set valuation_rate as previous valuation_rate # set valuation_rate as previous valuation_rate
row.valuation_rate = \ row.valuation_rate = previous_stock_value / flt(previous_stock_qty)
previous_stock_value / flt(previous_stock_qty)
_insert_entries() _insert_entries()
@ -234,8 +241,8 @@ class DocType(DocListController):
_insert_entries() _insert_entries()
def insert_entries(self, opts, row): def insert_entries(self, opts, row):
"""Insert Stock Ledger Entries""" """Insert Stock Ledger Entries"""
args = { args = webnotes._dict({
"doctype": "Stock Ledger Entry", "doctype": "Stock Ledger Entry",
"item_code": row.item_code, "item_code": row.item_code,
"warehouse": row.warehouse, "warehouse": row.warehouse,
@ -243,9 +250,10 @@ class DocType(DocListController):
"posting_time": self.doc.posting_time, "posting_time": self.doc.posting_time,
"voucher_type": self.doc.doctype, "voucher_type": self.doc.doctype,
"voucher_no": self.doc.name, "voucher_no": self.doc.name,
"company": webnotes.conn.get_default("company"), "company": self.doc.company,
"is_cancelled": "No", "is_cancelled": "No",
} "voucher_detail_no": row.voucher_detail_no
})
args.update(opts) args.update(opts)
# create stock ledger entry # create stock ledger entry
sle_wrapper = webnotes.bean([args]) sle_wrapper = webnotes.bean([args])
@ -254,17 +262,18 @@ class DocType(DocListController):
# update bin # update bin
webnotes.get_obj('Warehouse', row.warehouse).update_bin(args) webnotes.get_obj('Warehouse', row.warehouse).update_bin(args)
return sle_wrapper # append to entries
self.entries.append(args)
def delete_stock_ledger_entries(self): def delete_stock_ledger_entries(self):
""" Delete Stock Ledger Entries related to this Stock Reconciliation """ Delete Stock Ledger Entries related to this Stock Reconciliation
and repost future Stock Ledger Entries""" and repost future Stock Ledger Entries"""
existing_entries = webnotes.conn.sql("""select item_code, warehouse existing_entries = webnotes.conn.sql("""select item_code, warehouse
from `tabStock Ledger Entry` where voucher_type='Stock Reconciliation' from `tabStock Ledger Entry` where voucher_type='Stock Reconciliation'
and voucher_no=%s""", self.doc.name, as_dict=1) and voucher_no=%s""", self.doc.name, as_dict=1)
# delete entries # delete entries
webnotes.conn.sql("""delete from `tabStock Ledger Entry` webnotes.conn.sql("""delete from `tabStock Ledger Entry`
where voucher_type='Stock Reconciliation' and voucher_no=%s""", self.doc.name) where voucher_type='Stock Reconciliation' and voucher_no=%s""", self.doc.name)
@ -277,7 +286,33 @@ class DocType(DocListController):
"posting_date": self.doc.posting_date, "posting_date": self.doc.posting_date,
"posting_time": self.doc.posting_time "posting_time": self.doc.posting_time
}) })
def set_stock_value_difference(self):
"""stock_value_difference is the increment in the stock value"""
from stock.utils import get_buying_amount
item_list = [d.item_code for d in self.entries]
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
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)
def make_gl_entries(self):
if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")):
return
if not self.doc.expense_account:
msgprint(_("Please enter Expense Account"), raise_exception=1)
cost_center = "Auto Inventory Accounting - %s" % (self.company_abbr,)
super(DocType, self).make_gl_entries(self.doc.expense_account,
self.doc.stock_value_difference, cost_center)
@webnotes.whitelist() @webnotes.whitelist()
def upload(): def upload():

View File

@ -1,8 +1,8 @@
[ [
{ {
"creation": "2013-01-19 10:23:35", "creation": "2013-01-22 16:50:41",
"docstatus": 0, "docstatus": 0,
"modified": "2013-01-22 14:57:24", "modified": "2013-03-18 12:48:42",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -30,6 +30,7 @@
"permlevel": 0 "permlevel": 0
}, },
{ {
"amend": 1,
"cancel": 1, "cancel": 1,
"create": 1, "create": 1,
"doctype": "DocPerm", "doctype": "DocPerm",
@ -40,6 +41,7 @@
"permlevel": 0, "permlevel": 0,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Material Manager",
"submit": 1, "submit": 1,
"write": 1 "write": 1
}, },
@ -79,6 +81,22 @@
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
}, },
{
"doctype": "DocField",
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1
},
{
"depends_on": "eval:sys_defaults.auto_inventory_accounting",
"doctype": "DocField",
"fieldname": "expense_account",
"fieldtype": "Link",
"label": "Expense Account",
"options": "Account"
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "col1", "fieldname": "col1",
@ -119,12 +137,14 @@
"read_only": 1 "read_only": 1
}, },
{ {
"amend": 0, "doctype": "DocField",
"doctype": "DocPerm", "fieldname": "stock_value_difference",
"role": "Material Manager" "fieldtype": "Currency",
"hidden": 1,
"label": "Stock Value Difference",
"print_hide": 1
}, },
{ {
"doctype": "DocPerm", "doctype": "DocPerm"
"role": "System Manager"
} }
] ]

View File

@ -1,40 +1,15 @@
# ERPNext - web based ERP (http://erpnext.com) # ERPNext - web based ERP (http://erpnext.com)
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd # For license information, please see license.txt
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals from __future__ import unicode_literals
import unittest import webnotes, unittest
import webnotes
from webnotes.tests import insert_test_data
from webnotes.utils import flt from webnotes.utils import flt
import json import json
from accounts.utils import get_fiscal_year
company = webnotes.conn.get_default("company")
class TestStockReconciliation(unittest.TestCase): class TestStockReconciliation(unittest.TestCase):
def setUp(self):
webnotes.conn.begin()
self.insert_test_data()
def tearDown(self):
# print "Message Log:", "\n--\n".join(webnotes.message_log)
# print "Debug Log:", "\n--\n".join(webnotes.debug_log)
webnotes.conn.rollback()
def test_reco_for_fifo(self): def test_reco_for_fifo(self):
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
# [[qty, valuation_rate, posting_date, # [[qty, valuation_rate, posting_date,
# posting_time, expected_stock_value, bin_qty, bin_valuation]] # posting_time, expected_stock_value, bin_qty, bin_valuation]]
input_data = [ input_data = [
@ -53,28 +28,32 @@ class TestStockReconciliation(unittest.TestCase):
] ]
for d in input_data: for d in input_data:
self.cleanup_data()
self.insert_existing_sle("FIFO") self.insert_existing_sle("FIFO")
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
# check stock value
res = webnotes.conn.sql("""select stock_value from `tabStock Ledger Entry` res = webnotes.conn.sql("""select stock_value from `tabStock Ledger Entry`
where item_code = 'Android Jack D' and warehouse = 'Default Warehouse' where item_code = '_Test Item' and warehouse = '_Test Warehouse'
and posting_date = %s and posting_time = %s order by name desc limit 1""", and posting_date = %s and posting_time = %s order by name desc limit 1""",
(d[2], d[3])) (d[2], d[3]))
self.assertEqual(res and flt(res[0][0]) or 0, d[4]) self.assertEqual(res and flt(res[0][0]) or 0, d[4])
# check bin qty and stock value
bin = webnotes.conn.sql("""select actual_qty, stock_value from `tabBin` bin = webnotes.conn.sql("""select actual_qty, stock_value from `tabBin`
where item_code = 'Android Jack D' and warehouse = 'Default Warehouse'""") where item_code = '_Test Item' and warehouse = '_Test Warehouse'""")
self.assertEqual(bin and [flt(bin[0][0]), flt(bin[0][1])] or [], [d[5], d[6]]) self.assertEqual(bin and [flt(bin[0][0]), flt(bin[0][1])] or [], [d[5], d[6]])
# no gl entries
gl_entries = webnotes.conn.sql("""select name from `tabGL Entry`
where voucher_type = 'Stock Reconciliation' and voucher_no = %s""",
stock_reco.doc.name)
self.assertFalse(gl_entries)
self.tearDown()
self.setUp()
def test_reco_for_moving_average(self): def test_reco_for_moving_average(self):
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
# [[qty, valuation_rate, posting_date, # [[qty, valuation_rate, posting_date,
# posting_time, expected_stock_value, bin_qty, bin_valuation]] # posting_time, expected_stock_value, bin_qty, bin_valuation]]
input_data = [ input_data = [
@ -94,95 +73,194 @@ class TestStockReconciliation(unittest.TestCase):
] ]
for d in input_data: for d in input_data:
self.cleanup_data()
self.insert_existing_sle("Moving Average") self.insert_existing_sle("Moving Average")
stock_reco = self.submit_stock_reconciliation(d[0], d[1], d[2], d[3])
self.submit_stock_reconciliation(d[0], d[1], d[2], d[3]) # check stock value in sle
res = webnotes.conn.sql("""select stock_value from `tabStock Ledger Entry` res = webnotes.conn.sql("""select stock_value from `tabStock Ledger Entry`
where item_code = 'Android Jack D' and warehouse = 'Default Warehouse' where item_code = '_Test Item' and warehouse = '_Test Warehouse'
and posting_date = %s and posting_time = %s order by name desc limit 1""", and posting_date = %s and posting_time = %s order by name desc limit 1""",
(d[2], d[3])) (d[2], d[3]))
self.assertEqual(res and flt(res[0][0], 4) or 0, d[4]) self.assertEqual(res and flt(res[0][0], 4) or 0, d[4])
# bin qty and stock value
bin = webnotes.conn.sql("""select actual_qty, stock_value from `tabBin` bin = webnotes.conn.sql("""select actual_qty, stock_value from `tabBin`
where item_code = 'Android Jack D' and warehouse = 'Default Warehouse'""") where item_code = '_Test Item' and warehouse = '_Test Warehouse'""")
self.assertEqual(bin and [flt(bin[0][0]), flt(bin[0][1], 4)] or [], self.assertEqual(bin and [flt(bin[0][0]), flt(bin[0][1], 4)] or [],
[flt(d[5]), flt(d[6])]) [flt(d[5]), flt(d[6])])
self.tearDown() # no gl entries
self.setUp() gl_entries = webnotes.conn.sql("""select name from `tabGL Entry`
where voucher_type = 'Stock Reconciliation' and voucher_no = %s""",
stock_reco.doc.name)
self.assertFalse(gl_entries)
def test_reco_fifo_gl_entries(self):
webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
# [[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],
[15, 1000, "2012-12-26", "12:00", 3000],
[25, 900, "2012-12-26", "12:00", 10500],
[20, 500, "2012-12-26", "12:00", -2000],
["", 1000, "2012-12-26", "12:05", 3000],
[20, "", "2012-12-26", "12:05", 4000],
[10, 2000, "2012-12-26", "12:10", 8000],
[0, "", "2012-12-26", "12:10", -12000],
[50, 1000, "2013-01-01", "12:00", 50000],
[5, 1000, "2013-01-01", "12:00", 5000],
[1, 1000, "2012-12-01", "00:00", 1000],
]
for d in input_data:
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])
# cancel
stock_reco.cancel()
self.check_gl_entries(stock_reco.doc.name, -d[4], True)
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
def test_reco_moving_average_gl_entries(self):
webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
# [[qty, valuation_rate, posting_date,
# posting_time, stock_in_hand_debit]]
input_data = [
[50, 1000, "2012-12-26", "12:00", 36500],
[5, 1000, "2012-12-26", "12:00", -8500],
[15, 1000, "2012-12-26", "12:00", 1500],
[25, 900, "2012-12-26", "12:00", 9000],
[20, 500, "2012-12-26", "12:00", -3500],
["", 1000, "2012-12-26", "12:05", 1500],
[20, "", "2012-12-26", "12:05", 4500],
[10, 2000, "2012-12-26", "12:10", 6500],
[0, "", "2012-12-26", "12:10", -13500],
[50, 1000, "2013-01-01", "12:00", 50000],
[5, 1000, "2013-01-01", "12:00", 5000],
[1, 1000, "2012-12-01", "00:00", 1000],
]
for d in input_data:
self.cleanup_data()
self.insert_existing_sle("Moving Average")
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])
# cancel
stock_reco.cancel()
self.check_gl_entries(stock_reco.doc.name, -d[4], True)
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
def cleanup_data(self):
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
webnotes.conn.sql("delete from tabBin")
def submit_stock_reconciliation(self, qty, rate, posting_date, posting_time): def submit_stock_reconciliation(self, qty, rate, posting_date, posting_time):
return webnotes.bean([{ stock_reco = webnotes.bean([{
"doctype": "Stock Reconciliation", "doctype": "Stock Reconciliation",
"name": "RECO-001",
"__islocal": 1,
"posting_date": posting_date, "posting_date": posting_date,
"posting_time": posting_time, "posting_time": posting_time,
"fiscal_year": get_fiscal_year(posting_date)[0],
"company": "_Test Company",
"expense_account": "Stock Adjustment - _TC",
"reconciliation_json": json.dumps([ "reconciliation_json": json.dumps([
["Item Code", "Warehouse", "Quantity", "Valuation Rate"], ["Item Code", "Warehouse", "Quantity", "Valuation Rate"],
["Android Jack D", "Default Warehouse", qty, rate] ["_Test Item", "_Test Warehouse", qty, rate]
]), ]),
}]).submit() }])
stock_reco.insert()
stock_reco.submit()
return stock_reco
def insert_test_data(self): def check_gl_entries(self, voucher_no, stock_value_diff, cancel=None):
# create default warehouse stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company",
if not webnotes.conn.exists("Warehouse", "Default Warehouse"): "stock_in_hand_account")
webnotes.insert({"doctype": "Warehouse", debit_amount = stock_value_diff > 0 and stock_value_diff or 0.0
"warehouse_name": "Default Warehouse", credit_amount = stock_value_diff < 0 and abs(stock_value_diff) or 0.0
"warehouse_type": "Stores"})
expected_gl_entries = sorted([
# create UOM: Nos. [stock_in_hand_account, debit_amount, credit_amount],
if not webnotes.conn.exists("UOM", "Nos"): ["Stock Adjustment - _TC", credit_amount, debit_amount]
webnotes.insert({"doctype": "UOM", "uom_name": "Nos"}) ])
if cancel:
# create item groups and items expected_gl_entries = sorted([
insert_test_data("Item Group", [stock_in_hand_account, debit_amount, credit_amount],
sort_fn=lambda ig: (ig[0].get('parent_item_group'), ig[0].get('name'))) ["Stock Adjustment - _TC", credit_amount, debit_amount],
insert_test_data("Item") [stock_in_hand_account, credit_amount, debit_amount],
["Stock Adjustment - _TC", debit_amount, credit_amount]
])
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)
self.assertTrue(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)
def insert_existing_sle(self, valuation_method): def insert_existing_sle(self, valuation_method):
webnotes.conn.set_value("Item", "Android Jack D", "valuation_method", valuation_method) webnotes.conn.set_value("Item", "_Test Item", "valuation_method", valuation_method)
webnotes.conn.set_default("allow_negative_stock", 1) webnotes.conn.set_default("allow_negative_stock", 1)
existing_ledgers = [ existing_ledgers = [
{ {
"doctype": "Stock Ledger Entry", "__islocal": 1, "doctype": "Stock Ledger Entry", "__islocal": 1,
"voucher_type": "Stock Entry", "voucher_no": "TEST", "voucher_type": "Stock Entry", "voucher_no": "TEST",
"item_code": "Android Jack D", "warehouse": "Default Warehouse", "item_code": "_Test Item", "warehouse": "_Test Warehouse",
"posting_date": "2012-12-12", "posting_time": "01:00", "posting_date": "2012-12-12", "posting_time": "01:00",
"actual_qty": 20, "incoming_rate": 1000, "company": company "actual_qty": 20, "incoming_rate": 1000, "company": "_Test Company"
}, },
{ {
"doctype": "Stock Ledger Entry", "__islocal": 1, "doctype": "Stock Ledger Entry", "__islocal": 1,
"voucher_type": "Stock Entry", "voucher_no": "TEST", "voucher_type": "Stock Entry", "voucher_no": "TEST",
"item_code": "Android Jack D", "warehouse": "Default Warehouse", "item_code": "_Test Item", "warehouse": "_Test Warehouse",
"posting_date": "2012-12-15", "posting_time": "02:00", "posting_date": "2012-12-15", "posting_time": "02:00",
"actual_qty": 10, "incoming_rate": 700, "company": company "actual_qty": 10, "incoming_rate": 700, "company": "_Test Company"
}, },
{ {
"doctype": "Stock Ledger Entry", "__islocal": 1, "doctype": "Stock Ledger Entry", "__islocal": 1,
"voucher_type": "Stock Entry", "voucher_no": "TEST", "voucher_type": "Stock Entry", "voucher_no": "TEST",
"item_code": "Android Jack D", "warehouse": "Default Warehouse", "item_code": "_Test Item", "warehouse": "_Test Warehouse",
"posting_date": "2012-12-25", "posting_time": "03:00", "posting_date": "2012-12-25", "posting_time": "03:00",
"actual_qty": -15, "company": company "actual_qty": -15, "company": "_Test Company"
}, },
{ {
"doctype": "Stock Ledger Entry", "__islocal": 1, "doctype": "Stock Ledger Entry", "__islocal": 1,
"voucher_type": "Stock Entry", "voucher_no": "TEST", "voucher_type": "Stock Entry", "voucher_no": "TEST",
"item_code": "Android Jack D", "warehouse": "Default Warehouse", "item_code": "_Test Item", "warehouse": "_Test Warehouse",
"posting_date": "2012-12-31", "posting_time": "08:00", "posting_date": "2012-12-31", "posting_time": "08:00",
"actual_qty": -20, "company": company "actual_qty": -20, "company": "_Test Company"
}, },
{ {
"doctype": "Stock Ledger Entry", "__islocal": 1, "doctype": "Stock Ledger Entry", "__islocal": 1,
"voucher_type": "Stock Entry", "voucher_no": "TEST", "voucher_type": "Stock Entry", "voucher_no": "TEST",
"item_code": "Android Jack D", "warehouse": "Default Warehouse", "item_code": "_Test Item", "warehouse": "_Test Warehouse",
"posting_date": "2013-01-05", "posting_time": "07:00", "posting_date": "2013-01-05", "posting_time": "07:00",
"actual_qty": 15, "incoming_rate": 1200, "company": company "actual_qty": 15, "incoming_rate": 1200, "company": "_Test Company"
}, },
] ]
webnotes.get_obj("Stock Ledger").update_stock(existing_ledgers) webnotes.get_obj("Stock Ledger").update_stock(existing_ledgers)
test_dependencies = ["Item", "Warehouse"]