Conflicts:
	controllers/buying_controller.py
This commit is contained in:
Saurabh 2013-03-20 13:48:48 +05:30
commit 076535d979
31 changed files with 707 additions and 354 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

@ -444,6 +444,9 @@ class DocType(BuyingController):
# item gl entries # item gl entries
stock_item_and_auto_inventory_accounting = False stock_item_and_auto_inventory_accounting = False
if auto_inventory_accounting:
stock_acocunt = self.get_default_account("stock_received_but_not_billed")
for item in self.doclist.get({"parentfield": "entries"}): for item in self.doclist.get({"parentfield": "entries"}):
if auto_inventory_accounting and item.item_code in self.stock_items: if auto_inventory_accounting and item.item_code in self.stock_items:
if flt(item.valuation_rate): if flt(item.valuation_rate):
@ -455,7 +458,7 @@ class DocType(BuyingController):
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": "Stock Received But Not Billed - %s" % (self.company_abbr,), "account": stock_acocunt,
"against": self.doc.credit_to, "against": self.doc.credit_to,
"debit": flt(item.valuation_rate) * flt(item.conversion_factor) \ "debit": flt(item.valuation_rate) * flt(item.conversion_factor) \
* flt(item.qty), * flt(item.qty),
@ -480,7 +483,7 @@ class DocType(BuyingController):
# this will balance out valuation amount included in cost of goods sold # this will balance out valuation amount included in cost of goods sold
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": "Expenses Included In Valuation - %s" % (self.company_abbr,), "account": self.get_default_account("expenses_included_in_valuation"),
"cost_center": "Auto Inventory Accounting - %s" % self.company_abbr, "cost_center": "Auto Inventory Accounting - %s" % self.company_abbr,
"against": self.doc.credit_to, "against": self.doc.credit_to,
"credit": valuation_tax, "credit": valuation_tax,

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

@ -704,9 +704,9 @@ class DocType(SellingController):
if auto_inventory_accounting: if auto_inventory_accounting:
if cint(self.doc.is_pos) and cint(self.doc.update_stock): if cint(self.doc.is_pos) and cint(self.doc.update_stock):
stock_account = self.get_stock_in_hand_account() stock_account = self.get_default_account("stock_in_hand_account")
else: else:
stock_account = "Stock Delivered But Not Billed - %s" % (self.company_abbr,) stock_account = self.get_default_account("stock_delivered_but_not_billed")
for item in self.doclist.get({"parentfield": "entries"}): for item in self.doclist.get({"parentfield": "entries"}):
# income account gl entries # income account gl entries
@ -794,7 +794,8 @@ class DocType(SellingController):
stock_ledger_entries = item_sales_bom = None stock_ledger_entries = item_sales_bom = None
for item in self.doclist.get({"parentfield": "entries"}): for item in self.doclist.get({"parentfield": "entries"}):
if item.item_code in self.stock_items: if item.item_code in self.stock_items or \
(item_sales_bom and item_sales_bom.get(item.item_code)):
item.buying_amount = self.get_item_buying_amount(item, stock_ledger_entries, item.buying_amount = self.get_item_buying_amount(item, stock_ledger_entries,
item_sales_bom) item_sales_bom)
webnotes.conn.set_value("Sales Invoice Item", item.name, webnotes.conn.set_value("Sales Invoice Item", item.name,
@ -804,8 +805,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):
@ -74,16 +75,15 @@ class AccountsController(TransactionBase):
"advance_amount": flt(d.amount), "advance_amount": flt(d.amount),
"allocate_amount": 0 "allocate_amount": 0
}) })
def get_stock_in_hand_account(self):
stock_in_hand_account = webnotes.conn.get_value("Company", self.doc.company, "stock_in_hand_account")
if not stock_in_hand_account: def get_default_account(self, account_for):
msgprint(_("Missing") + ": " account = webnotes.conn.get_value("Company", self.doc.company, account_for)
+ _(webnotes.get_doctype("company").get_label("stock_in_hand_account") if not account:
+ " " + _("for Company") + " " + self.doc.company), raise_exception=True) msgprint(_("Please mention default account for '") +
_(webnotes.get_doctype("company").get_label(account_for) +
return stock_in_hand_account _("' in Company: ") + self.doc.company), raise_exception=True)
return account
@property @property
def stock_items(self): def stock_items(self):
@ -92,7 +92,7 @@ class AccountsController(TransactionBase):
self._stock_items = [r[0] for r in webnotes.conn.sql("""select name self._stock_items = [r[0] for r in webnotes.conn.sql("""select name
from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \ from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \
(", ".join((["%s"]*len(item_codes))),), item_codes)] (", ".join((["%s"]*len(item_codes))),), item_codes)]
return self._stock_items return self._stock_items
@property @property
@ -100,4 +100,9 @@ class AccountsController(TransactionBase):
if not hasattr(self, "_abbr"): if not hasattr(self, "_abbr"):
self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr") self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr")
return self._abbr return self._abbr
@webnotes.whitelist()
def get_default_account(account_for, company):
return webnotes.conn.get_value("Company", company, account_for)

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):
super(BuyingController, self).validate() super(BuyingController, self).validate()
if self.meta.get_field("currency"): if self.meta.get_field("currency"):

View File

@ -19,9 +19,9 @@ import webnotes
from webnotes.utils import cint from webnotes.utils import cint
from setup.utils import get_company_currency from setup.utils import get_company_currency
from controllers.accounts_controller import AccountsController from controllers.stock_controller import StockController
class SellingController(AccountsController): class SellingController(StockController):
def validate(self): def validate(self):
super(SellingController, self).validate() super(SellingController, self).validate()
self.set_total_in_words() self.set_total_in_words()
@ -39,26 +39,3 @@ class SellingController(AccountsController):
if self.meta.get_field("in_words_export"): if self.meta.get_field("in_words_export"):
self.doc.in_words_export = money_in_words(disable_rounded_total and self.doc.in_words_export = money_in_words(disable_rounded_total and
self.doc.grand_total_export or self.doc.rounded_total_export, self.doc.currency) self.doc.grand_total_export or self.doc.rounded_total_export, self.doc.currency)
def get_stock_ledger_entries(self):
item_list, warehouse_list = self.get_distinct_item_warehouse()
if item_list and warehouse_list:
return webnotes.conn.sql("""select item_code, voucher_type, voucher_no,
voucher_detail_no, posting_date, posting_time, stock_value,
warehouse, actual_qty as qty from `tabStock Ledger Entry`
where ifnull(`is_cancelled`, "No") = "No" and company = %s
and item_code in (%s) and warehouse in (%s)
order by item_code desc, warehouse desc, posting_date desc,
posting_time desc, name desc""" %
('%s', ', '.join(['%s']*len(item_list)), ', '.join(['%s']*len(warehouse_list))),
tuple([self.doc.company] + item_list + warehouse_list), as_dict=1)
def get_distinct_item_warehouse(self):
item_list = []
warehouse_list = []
for item in self.doclist.get({"parentfield": self.fname}) \
+ self.doclist.get({"parentfield": "packing_details"}):
item_list.append(item.item_code)
warehouse_list.append(item.warehouse)
return list(set(item_list)), list(set(warehouse_list))

View File

@ -0,0 +1,71 @@
# ERPNext - web based ERP (http://erpnext.com)
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
#
# 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
import webnotes
from controllers.accounts_controller import AccountsController
class StockController(AccountsController):
def make_gl_entries(self, against_stock_account, amount, cost_center=None):
stock_in_hand_account = self.get_default_account("stock_in_hand_account")
if amount:
gl_entries = [
# stock in hand account
self.get_gl_dict({
"account": stock_in_hand_account,
"against": against_stock_account,
"debit": amount,
"remarks": self.doc.remarks or "Accounting Entry for Stock",
}, self.doc.docstatus == 2),
# account against stock in hand
self.get_gl_dict({
"account": against_stock_account,
"against": stock_in_hand_account,
"credit": amount,
"cost_center": cost_center or None,
"remarks": self.doc.remarks or "Accounting Entry for Stock",
}, self.doc.docstatus == 2),
]
from accounts.general_ledger import make_gl_entries
make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2)
def get_stock_ledger_entries(self, item_list=None, warehouse_list=None):
if not (item_list and warehouse_list):
item_list, warehouse_list = self.get_distinct_item_warehouse()
if item_list and warehouse_list:
return webnotes.conn.sql("""select item_code, voucher_type, voucher_no,
voucher_detail_no, posting_date, posting_time, stock_value,
warehouse, actual_qty as qty from `tabStock Ledger Entry`
where ifnull(`is_cancelled`, "No") = "No" and company = %s
and item_code in (%s) and warehouse in (%s)
order by item_code desc, warehouse desc, posting_date desc,
posting_time desc, name desc""" %
('%s', ', '.join(['%s']*len(item_list)), ', '.join(['%s']*len(warehouse_list))),
tuple([self.doc.company] + item_list + warehouse_list), as_dict=1)
def get_distinct_item_warehouse(self):
item_list = []
warehouse_list = []
for item in self.doclist.get({"parentfield": self.fname}) \
+ self.doclist.get({"parentfield": "packing_details"}):
item_list.append(item.item_code)
warehouse_list.append(item.warehouse)
return list(set(item_list)), list(set(warehouse_list))

View File

@ -58,3 +58,43 @@ cur_frm.fields_dict.receivables_group.get_query = function(doc) {
cur_frm.fields_dict.payables_group.get_query = function(doc) { cur_frm.fields_dict.payables_group.get_query = function(doc) {
return 'SELECT `tabAccount`.name FROM `tabAccount` WHERE `tabAccount`.company = "'+doc.name+'" AND `tabAccount`.group_or_ledger = "Group" AND `tabAccount`.docstatus != 2 AND `tabAccount`.%(key)s LIKE "%s" ORDER BY `tabAccount`.name LIMIT 50'; return 'SELECT `tabAccount`.name FROM `tabAccount` WHERE `tabAccount`.company = "'+doc.name+'" AND `tabAccount`.group_or_ledger = "Group" AND `tabAccount`.docstatus != 2 AND `tabAccount`.%(key)s LIKE "%s" ORDER BY `tabAccount`.name LIMIT 50';
} }
cur_frm.fields_dict["stock_in_hand_account"].get_query = function(doc) {
return {
"query": "accounts.utils.get_account_list",
"filters": {
"is_pl_account": "No",
"debit_or_credit": "Debit",
"company": doc.name
}
}
}
cur_frm.fields_dict["stock_adjustment_account"].get_query = function(doc) {
return {
"query": "accounts.utils.get_account_list",
"filters": {
"is_pl_account": "Yes",
"debit_or_credit": "Debit",
"company": doc.name
}
}
}
cur_frm.fields_dict["expenses_included_in_valuation"].get_query =
cur_frm.fields_dict["stock_adjustment_account"].get_query;
cur_frm.fields_dict["stock_delivered_but_not_billed"].get_query =
cur_frm.fields_dict["stock_in_hand_account"].get_query;
cur_frm.fields_dict["stock_received_but_not_billed"].get_query = function(doc) {
return {
"query": "accounts.utils.get_account_list",
"filters": {
"is_pl_account": "No",
"debit_or_credit": "Credit",
"company": doc.name
}
}
}

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
@ -47,10 +47,11 @@ class DocType:
['Loans and Advances (Assets)','Current Assets','Group','No','','Debit',self.doc.name,''], ['Loans and Advances (Assets)','Current Assets','Group','No','','Debit',self.doc.name,''],
['Securities and Deposits','Current Assets','Group','No','','Debit',self.doc.name,''], ['Securities and Deposits','Current Assets','Group','No','','Debit',self.doc.name,''],
['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 Assets','Current Assets','Group','No','','Debit',self.doc.name,''],
['Stock','Stock In Hand','Ledger','No','','Debit',self.doc.name,''], ['Stock In Hand','Stock Assets','Ledger','No','','Debit',self.doc.name,''],
['Stock Delivered But Not Billed', 'Stock Assets', '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,''],
['Fixed Assets','Application of Funds (Assets)','Group','No','','Debit',self.doc.name,''], ['Fixed Assets','Application of Funds (Assets)','Group','No','','Debit',self.doc.name,''],
['Capital Equipments','Fixed Assets','Ledger','No','Fixed Asset Account','Debit',self.doc.name,''], ['Capital Equipments','Fixed Assets','Ledger','No','Fixed Asset Account','Debit',self.doc.name,''],
['Computers','Fixed Assets','Ledger','No','Fixed Asset Account','Debit',self.doc.name,''], ['Computers','Fixed Assets','Ledger','No','Fixed Asset Account','Debit',self.doc.name,''],
@ -62,9 +63,10 @@ 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,''],
['Expenses Included In Valuation', "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,12 +103,14 @@ 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,''],
['Unsecured Loans','Loans (Liabilities)','Group','No','','Credit',self.doc.name,''], ['Unsecured Loans','Loans (Liabilities)','Group','No','','Credit',self.doc.name,''],
['Bank Overdraft Account','Loans (Liabilities)','Group','No','','Credit',self.doc.name,''], ['Bank Overdraft Account','Loans (Liabilities)','Group','No','','Credit',self.doc.name,''],
['Stock Received But Not Billed','Current Liabilities','Ledger','No','','Credit',self.doc.name,''],
['Temporary Accounts (Liabilities)','Source of Funds (Liabilities)','Group','No','','Credit',self.doc.name,''], ['Temporary Accounts (Liabilities)','Source of Funds (Liabilities)','Group','No','','Credit',self.doc.name,''],
['Temporary Account (Liabilities)','Temporary Accounts (Liabilities)','Ledger','No','','Credit',self.doc.name,''] ['Temporary Account (Liabilities)','Temporary Accounts (Liabilities)','Ledger','No','','Credit',self.doc.name,'']
] ]
@ -186,14 +190,35 @@ 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)
if not self.doc.expenses_included_in_valuation and webnotes.conn.exists("Account",
"Expenses Included In Valuation - " + self.doc.abbr):
webnotes.conn.set(self.doc, "expenses_included_in_valuation",
"Expenses Included In Valuation - " + self.doc.abbr)
# Create default cost center # Create default cost center
# --------------------------------------------------- # ---------------------------------------------------
@ -228,7 +253,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 +283,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-19 12:52:00",
"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,57 @@
"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": "expenses_included_in_valuation",
"fieldtype": "Link",
"label": "Expenses Included In Valuation",
"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

@ -1,8 +1,8 @@
[ [
{ {
"creation": "2013-03-08 15:37:09", "creation": "2013-02-21 14:54:43",
"docstatus": 0, "docstatus": 0,
"modified": "2013-03-12 18:48:53", "modified": "2013-03-19 14:46:49",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -410,7 +410,7 @@
"fieldname": "emp_created_by", "fieldname": "emp_created_by",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Employee Records to be created by ", "label": "Employee Records to be created by ",
"options": "\nNaming Series\nEmployee Number" "options": "Naming Series\nEmployee Number"
}, },
{ {
"doctype": "DocField", "doctype": "DocField",

View File

@ -16,15 +16,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes import _ from webnotes.utils import add_days, cint,flt, nowdate, get_url_to_form, formatdate
from webnotes import msgprint, _
from webnotes.utils import add_days, cint, cstr, flt, now, nowdate, \
get_url_to_form, formatdate
from webnotes.model import db_exists
from webnotes.model.doc import Document, addchild
from webnotes.model.bean import copy_doclist
from webnotes.model.code import get_obj
from webnotes import msgprint
sql = webnotes.conn.sql sql = webnotes.conn.sql
import webnotes.defaults import webnotes.defaults
@ -61,7 +54,7 @@ class DocType:
from stock.stock_ledger import update_entries_after from stock.stock_ledger import update_entries_after
if not args.get("posting_date"): if not args.get("posting_date"):
posting_date = nowdate() args["posting_date"] = nowdate()
# update valuation and qty after transaction for post dated entry # update valuation and qty after transaction for post dated entry
update_entries_after({ update_entries_after({
@ -108,11 +101,10 @@ class DocType:
#check if re-order is required #check if re-order is required
item_reorder = webnotes.conn.get("Item Reorder", item_reorder = webnotes.conn.get("Item Reorder",
{"parent": self.doc.item_code, "warehouse": self.doc.warehouse}) {"parent": self.doc.item_code, "warehouse": self.doc.warehouse})
if item_reorder: if item_reorder:
reorder_level = item_reorder.warehouse_reorder_level reorder_level = item_reorder.warehouse_reorder_level
reorder_qty = item_reorder.warehouse_reorder_qty reorder_qty = item_reorder.warehouse_reorder_qty
material_request_type = item_reorder.material_request_type material_request_type = item_reorder.material_request_type or "Purchase"
else: else:
reorder_level, reorder_qty = webnotes.conn.get_value("Item", self.doc.item_code, reorder_level, reorder_qty = webnotes.conn.get_value("Item", self.doc.item_code,
["re_order_level", "re_order_qty"]) ["re_order_level", "re_order_qty"])
@ -123,7 +115,7 @@ class DocType:
material_request_type) material_request_type)
def create_material_request(self, doc_type, doc_name, reorder_level, reorder_qty, def create_material_request(self, doc_type, doc_name, reorder_level, reorder_qty,
material_request_type): material_request_type="Purchase"):
""" Create indent on reaching reorder level """ """ Create indent on reaching reorder level """
defaults = webnotes.defaults.get_defaults() defaults = webnotes.defaults.get_defaults()
item = webnotes.doc("Item", self.doc.item_code) item = webnotes.doc("Item", self.doc.item_code)
@ -151,7 +143,6 @@ class DocType:
"qty": reorder_qty, "qty": reorder_qty,
"brand": item.brand, "brand": item.brand,
}]) }])
mr.insert() mr.insert()
mr.submit() mr.submit()

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,11 +400,14 @@ 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, if item.item_code in self.stock_items or \
self.doc.doctype, self.doc.name, item.name, stock_ledger_entries, (item_sales_bom and item_sales_bom.get(item.item_code)):
item_sales_bom) buying_amount = get_buying_amount(item.item_code, item.warehouse, -1*item.qty,
webnotes.conn.set_value("Delivery Note Item", item.name, "buying_amount", self.doc.doctype, self.doc.name, item.name, stock_ledger_entries,
item.buying_amount) 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",
item.buying_amount)
self.validate_warehouse() self.validate_warehouse()
@ -420,32 +422,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 = self.get_default_account("stock_delivered_but_not_billed")
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

@ -1,8 +1,8 @@
[ [
{ {
"creation": "2013-02-22 01:28:01", "creation": "2013-03-07 11:42:59",
"docstatus": 0, "docstatus": 0,
"modified": "2013-03-07 07:03:22", "modified": "2013-03-19 12:22:44",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -20,7 +20,8 @@
"parent": "Item Reorder", "parent": "Item Reorder",
"parentfield": "fields", "parentfield": "fields",
"parenttype": "DocType", "parenttype": "DocType",
"permlevel": 0 "permlevel": 0,
"read_only": 0
}, },
{ {
"doctype": "DocType", "doctype": "DocType",
@ -38,7 +39,8 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "warehouse_reorder_level", "fieldname": "warehouse_reorder_level",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Re-order Level" "label": "Re-order Level",
"reqd": 1
}, },
{ {
"doctype": "DocField", "doctype": "DocField",
@ -51,6 +53,7 @@
"fieldname": "material_request_type", "fieldname": "material_request_type",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Material Request Type", "label": "Material Request Type",
"options": "Purchase\nTransfer" "options": "Purchase\nTransfer",
"reqd": 1
} }
] ]

View File

@ -28,18 +28,10 @@ cur_frm.fields_dict['item_details'].grid.get_field('item_code').get_query = func
// Fetch item details // Fetch item details
cur_frm.cscript.item_code = function(doc, cdt, cdn) { cur_frm.add_fetch("item_code", "item_name", "item_name");
if(locals[cdt][cdn].item_code) { cur_frm.add_fetch("item_code", "stock_uom", "stock_uom");
$c_obj(make_doclist(cdt, cdn), 'get_item_details', doc.delivery_note, function(r, rt) { cur_frm.add_fetch("item_code", "net_weight", "net_weight");
if(r.exc) { cur_frm.add_fetch("item_code", "weight_uom", "weight_uom");
msgprint(r.exc);
} else {
refresh_field('item_details');
}
});
}
}
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
if(doc.delivery_note && doc.__islocal) { if(doc.delivery_note && doc.__islocal) {

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 = self.get_default_account("stock_received_but_not_billed")
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,33 @@ wn.require("public/app/js/controllers/stock_controller.js");
wn.provide("erpnext.stock"); wn.provide("erpnext.stock");
erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
onload: function() {
this.set_default_account();
},
set_default_account: function() {
var me = this;
if (sys_defaults.auto_inventory_accounting && !this.frm.doc.expense_adjustment_account) {
if (this.frm.doc.purpose == "Sales Return")
account_for = "stock_delivered_but_not_billed";
else if (this.frm.doc.purpose == "Purchase Return")
account_for = "stock_received_but_not_billed";
else account_for = "stock_adjustment_account";
this.frm.call({
method: "controllers.accounts_controller.get_default_account",
args: {
"account_for": account_for,
"company": this.frm.doc.company
},
callback: function(r) {
if (!r.exc) me.frm.set_value("expense_adjustment_account", r.message);
}
});
}
},
setup: function() { setup: function() {
var me = this; var me = this;
@ -49,6 +76,16 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
} }
}; };
if (sys_defaults.auto_inventory_accounting) {
this.frm.add_fetch("company", "expense_adjustment_account", "stock_adjustment_account");
this.frm.fields_dict["expense_adjustment_account"].get_query = function() {
return {
"query": "accounts.utils.get_account_list",
"filters": { "company": me.frm.doc.company }
}
}
}
}, },
onload_post_render: function() { onload_post_render: function() {

View File

@ -31,9 +31,9 @@ sql = webnotes.conn.sql
class NotUpdateStockError(webnotes.ValidationError): pass class NotUpdateStockError(webnotes.ValidationError): pass
class StockOverReturnError(webnotes.ValidationError): pass class StockOverReturnError(webnotes.ValidationError): pass
from controllers.accounts_controller import AccountsController from controllers.stock_controller import StockController
class DocType(AccountsController): class DocType(StockController):
def __init__(self, doc, doclist=[]): def __init__(self, doc, doclist=[]):
self.doc = doc self.doc = doc
self.doclist = doclist self.doclist = doclist
@ -171,41 +171,24 @@ class DocType(AccountsController):
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") if not self.doc.expense_adjustment_account:
stock_in_hand_account = self.get_stock_in_hand_account() webnotes.msgprint(_("Please enter Expense/Adjustment Account"), raise_exception=1)
total_valuation_amount = self.get_total_valuation_amount()
if total_valuation_amount:
gl_entries = [
# debit stock in hand account
self.get_gl_dict({
"account": stock_in_hand_account,
"against": "Stock Adjustment - %s" % abbr,
"debit": total_valuation_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 Adjustment - %s" % abbr,
"against": stock_in_hand_account,
"credit": total_valuation_amount,
"cost_center": "Auto Inventory Accounting - %s" % abbr,
"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)
cost_center = "Auto Inventory Accounting - %s" % (self.company_abbr,)
total_valuation_amount = self.get_total_valuation_amount()
super(DocType, self).make_gl_entries(self.doc.expense_adjustment_account,
total_valuation_amount, cost_center)
def get_total_valuation_amount(self): def get_total_valuation_amount(self):
total_valuation_amount = 0 total_valuation_amount = 0
for item in self.doclist.get({"parentfield": "mtn_details"}): for item in self.doclist.get({"parentfield": "mtn_details"}):
if item.t_warehouse and not item.s_warehouse: if item.t_warehouse and not item.s_warehouse:
total_valuation_amount += flt(item.incoming_rate) * flt(item.transfer_qty) total_valuation_amount += flt(item.incoming_rate) * flt(item.transfer_qty)
if item.s_warehouse and not item.t_warehouse: if item.s_warehouse and not item.t_warehouse:
total_valuation_amount -= flt(item.incoming_rate) * flt(item.transfer_qty) total_valuation_amount -= flt(item.incoming_rate) * flt(item.transfer_qty)
return total_valuation_amount return total_valuation_amount
def get_stock_and_rate(self): def get_stock_and_rate(self):
@ -938,4 +921,4 @@ def make_return_jv_from_purchase_receipt(se, ref):
result = [parent] + [{"account": account} for account in children] result = [parent] + [{"account": account} for account in children]
return result return result

View File

@ -60,6 +60,7 @@
"fieldtype": "Column Break", "fieldtype": "Column Break",
"oldfieldtype": "Column Break", "oldfieldtype": "Column Break",
"print_width": "50%", "print_width": "50%",
"read_only": 0,
"width": "50%" "width": "50%"
}, },
{ {
@ -76,6 +77,7 @@
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nSTE", "options": "\nSTE",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0 "search_index": 0
@ -95,6 +97,7 @@
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nManufacture/Repack\nSubcontract\nSales Return\nPurchase Return", "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nManufacture/Repack\nSubcontract\nSales Return\nPurchase Return",
"print_hide": 0, "print_hide": 0,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0 "search_index": 0
@ -152,6 +155,7 @@
"fieldtype": "Column Break", "fieldtype": "Column Break",
"oldfieldtype": "Column Break", "oldfieldtype": "Column Break",
"print_width": "50%", "print_width": "50%",
"read_only": 0,
"width": "50%" "width": "50%"
}, },
{ {
@ -169,6 +173,7 @@
"oldfieldname": "posting_date", "oldfieldname": "posting_date",
"oldfieldtype": "Date", "oldfieldtype": "Date",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 1 "search_index": 1
@ -185,16 +190,26 @@
"oldfieldname": "posting_time", "oldfieldname": "posting_time",
"oldfieldtype": "Time", "oldfieldtype": "Time",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0 "search_index": 0
}, },
{
"depends_on": "eval:sys_defaults.auto_inventory_accounting",
"doctype": "DocField",
"fieldname": "expense_adjustment_account",
"fieldtype": "Link",
"label": "Expense/Adjustment Account",
"options": "Account"
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "items_section", "fieldname": "items_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Items", "label": "Items",
"oldfieldtype": "Section Break" "oldfieldtype": "Section Break",
"read_only": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -210,6 +225,7 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Warehouse", "options": "Warehouse",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -217,7 +233,8 @@
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "cb0", "fieldname": "cb0",
"fieldtype": "Column Break" "fieldtype": "Column Break",
"read_only": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -233,6 +250,7 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Warehouse", "options": "Warehouse",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -241,7 +259,8 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "sb0", "fieldname": "sb0",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"options": "Simple" "options": "Simple",
"read_only": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -256,6 +275,7 @@
"oldfieldtype": "Table", "oldfieldtype": "Table",
"options": "Stock Entry Detail", "options": "Stock Entry Detail",
"print_hide": 0, "print_hide": 0,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -268,14 +288,16 @@
"label": "Get Stock and Rate", "label": "Get Stock and Rate",
"oldfieldtype": "Button", "oldfieldtype": "Button",
"options": "get_stock_and_rate", "options": "get_stock_and_rate",
"print_hide": 1 "print_hide": 1,
"read_only": 0
}, },
{ {
"depends_on": "eval:(doc.purpose!==\"Sales Return\" || doc.purpose!==\"Purchase Return\")", "depends_on": "eval:(doc.purpose!==\"Sales Return\" || doc.purpose!==\"Purchase Return\")",
"doctype": "DocField", "doctype": "DocField",
"fieldname": "sb1", "fieldname": "sb1",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Reference" "label": "Reference",
"read_only": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -291,6 +313,7 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Production Order", "options": "Production Order",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 1 "search_index": 1
@ -301,7 +324,8 @@
"fieldname": "bom_no", "fieldname": "bom_no",
"fieldtype": "Link", "fieldtype": "Link",
"label": "BOM No", "label": "BOM No",
"options": "BOM" "options": "BOM",
"read_only": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -317,6 +341,7 @@
"oldfieldname": "fg_completed_qty", "oldfieldname": "fg_completed_qty",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -324,7 +349,8 @@
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "cb1", "fieldname": "cb1",
"fieldtype": "Column Break" "fieldtype": "Column Break",
"read_only": 0
}, },
{ {
"default": "1", "default": "1",
@ -333,7 +359,8 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "use_multi_level_bom", "fieldname": "use_multi_level_bom",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Use Multi-Level BOM" "label": "Use Multi-Level BOM",
"read_only": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -347,6 +374,7 @@
"no_copy": 0, "no_copy": 0,
"oldfieldtype": "Button", "oldfieldtype": "Button",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -356,7 +384,8 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "contact_section", "fieldname": "contact_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Contact Info" "label": "Contact Info",
"read_only": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -372,6 +401,7 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Supplier", "options": "Supplier",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -407,6 +437,7 @@
"oldfieldname": "supplier_address", "oldfieldname": "supplier_address",
"oldfieldtype": "Small Text", "oldfieldtype": "Small Text",
"print_hide": 0, "print_hide": 0,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -425,6 +456,7 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Customer", "options": "Customer",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -460,6 +492,7 @@
"oldfieldname": "customer_address", "oldfieldname": "customer_address",
"oldfieldtype": "Small Text", "oldfieldtype": "Small Text",
"print_hide": 0, "print_hide": 0,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -469,13 +502,15 @@
"fieldname": "more_info", "fieldname": "more_info",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "More Info", "label": "More Info",
"oldfieldtype": "Section Break" "oldfieldtype": "Section Break",
"read_only": 0
}, },
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "col4", "fieldname": "col4",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"print_width": "50%", "print_width": "50%",
"read_only": 0,
"width": "50%" "width": "50%"
}, },
{ {
@ -486,7 +521,8 @@
"label": "Project Name", "label": "Project Name",
"oldfieldname": "project_name", "oldfieldname": "project_name",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Project" "options": "Project",
"read_only": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -501,6 +537,7 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Print Heading", "options": "Print Heading",
"print_hide": 0, "print_hide": 0,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -518,6 +555,7 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Company", "options": "Company",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0 "search_index": 0
@ -527,6 +565,7 @@
"fieldname": "col5", "fieldname": "col5",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"print_width": "50%", "print_width": "50%",
"read_only": 0,
"width": "50%" "width": "50%"
}, },
{ {
@ -559,6 +598,7 @@
"oldfieldname": "remarks", "oldfieldname": "remarks",
"oldfieldtype": "Text", "oldfieldtype": "Text",
"print_hide": 1, "print_hide": 1,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0 "search_index": 0
@ -570,5 +610,13 @@
{ {
"doctype": "DocPerm", "doctype": "DocPerm",
"role": "Manufacturing User" "role": "Manufacturing User"
},
{
"doctype": "DocPerm",
"role": "Manufacturing Manager"
},
{
"doctype": "DocPerm",
"role": "Material Manager"
} }
] ]

View File

@ -560,6 +560,7 @@ test_records = [
"posting_time": "17:14:24", "posting_time": "17:14:24",
"purpose": "Material Receipt", "purpose": "Material Receipt",
"fiscal_year": "_Test Fiscal Year 2013", "fiscal_year": "_Test Fiscal Year 2013",
"expense_adjustment_account": "Stock Adjustment - _TC"
}, },
{ {
"conversion_factor": 1.0, "conversion_factor": 1.0,
@ -582,6 +583,7 @@ test_records = [
"posting_time": "17:15", "posting_time": "17:15",
"purpose": "Material Issue", "purpose": "Material Issue",
"fiscal_year": "_Test Fiscal Year 2013", "fiscal_year": "_Test Fiscal Year 2013",
"expense_adjustment_account": "Stock Adjustment - _TC"
}, },
{ {
"conversion_factor": 1.0, "conversion_factor": 1.0,
@ -604,6 +606,7 @@ test_records = [
"posting_time": "17:14:24", "posting_time": "17:14:24",
"purpose": "Material Transfer", "purpose": "Material Transfer",
"fiscal_year": "_Test Fiscal Year 2013", "fiscal_year": "_Test Fiscal Year 2013",
"expense_adjustment_account": "Stock Adjustment - _TC"
}, },
{ {
"conversion_factor": 1.0, "conversion_factor": 1.0,

View File

@ -18,6 +18,44 @@ 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({
onload: function() {
this.set_default_expense_account();
},
set_default_expense_account: function() {
var me = this;
if (sys_defaults.auto_inventory_accounting && !this.frm.doc.expense_account) {
this.frm.call({
method: "controllers.accounts_controller.get_default_account",
args: {
"account_for": "stock_adjustment_account",
"company": this.frm.doc.company
},
callback: function(r) {
if (!r.exc) me.frm.set_value("expense_account", r.message);
}
});
}
},
setup: function() {
var me = this;
this.frm.add_fetch("company", "expense_account", "stock_adjustment_account");
this.frm.fields_dict["expense_account"].get_query = function() {
return {
"query": "accounts.utils.get_account_list",
"filters": {
"is_pl_account": "Yes",
"debit_or_credit": "Debit",
"company": me.frm.doc.company
}
}
}
},
refresh: function() { refresh: function() {
if(this.frm.doc.docstatus===0) { if(this.frm.doc.docstatus===0) {
this.show_download_template(); this.show_download_template();

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"]

View File

@ -72,7 +72,7 @@ def update_entries_after(args, verbose=1):
(qty_after_transaction * valuation_rate) or 0 (qty_after_transaction * valuation_rate) or 0
else: else:
stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue)) stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue))
# print sle.posting_date, sle.actual_qty, sle.incoming_rate, stock_queue, stock_value
# update current sle # update current sle
webnotes.conn.sql("""update `tabStock Ledger Entry` webnotes.conn.sql("""update `tabStock Ledger Entry`
set qty_after_transaction=%s, valuation_rate=%s, stock_queue=%s, set qty_after_transaction=%s, valuation_rate=%s, stock_queue=%s,

View File

@ -165,8 +165,8 @@ def get_warehouse_list(doctype, txt, searchfield, start, page_len, filters):
return wlist return wlist
def get_buying_amount(item_code, warehouse, qty, voucher_type, voucher_no, voucher_detail_no, def get_buying_amount(item_code, warehouse, qty, voucher_type, voucher_no, voucher_detail_no,
stock_ledger_entries, item_sales_bom): stock_ledger_entries, item_sales_bom=None):
if item_sales_bom.get(item_code): if item_sales_bom and item_sales_bom.get(item_code):
# sales bom item # sales bom item
buying_amount = 0.0 buying_amount = 0.0
for bom_item in item_sales_bom[item_code]: for bom_item in item_sales_bom[item_code]:
@ -182,13 +182,15 @@ def _get_buying_amount(voucher_type, voucher_no, item_row, item_code, warehouse,
stock_ledger_entries): stock_ledger_entries):
for i, sle in enumerate(stock_ledger_entries): for i, sle in enumerate(stock_ledger_entries):
if sle.voucher_type == voucher_type and sle.voucher_no == voucher_no and \ if sle.voucher_type == voucher_type and sle.voucher_no == voucher_no and \
len(stock_ledger_entries) > i+1: (sle.voucher_detail_no == item_row or (sle.voucher_type != "Stock Reconciliation"
if (sle.voucher_detail_no == item_row) or \ and sle.item_code == item_code and sle.warehouse == warehouse and flt(sle.qty) == qty)):
(sle.item_code == item_code and sle.warehouse == warehouse and \ # print "previous_sle", stock_ledger_entries[i+1]
abs(flt(sle.qty)) == qty): # print "current sle", sle
buying_amount = flt(stock_ledger_entries[i+1].stock_value) - \ previous_stock_value = len(stock_ledger_entries) > i+1 and \
flt(sle.stock_value) flt(stock_ledger_entries[i+1].stock_value) or 0.0
return buying_amount
buying_amount = previous_stock_value - flt(sle.stock_value)
return buying_amount
return 0.0 return 0.0
def get_sales_bom(): def get_sales_bom():