[fix] [minor] auto accounting for stock transactions
This commit is contained in:
		
							parent
							
								
									d85d63bb81
								
							
						
					
					
						commit
						bb77756069
					
				| @ -12,19 +12,6 @@ class DocType: | ||||
| 	def __init__(self, d, dl): | ||||
| 		self.doc, self.doclist = d, dl | ||||
| 
 | ||||
| 	def validate(self): | ||||
| 		self.validate_auto_accounting_for_stock() | ||||
| 		 | ||||
| 	def validate_auto_accounting_for_stock(self): | ||||
| 		if cint(self.doc.auto_accounting_for_stock) == 1: | ||||
| 			previous_val = cint(webnotes.conn.get_value("Accounts Settings",  | ||||
| 				None, "auto_accounting_for_stock")) | ||||
| 			if cint(self.doc.auto_accounting_for_stock) != previous_val: | ||||
| 				from accounts.utils import validate_stock_and_account_balance, \ | ||||
| 					create_stock_in_hand_jv | ||||
| 				validate_stock_and_account_balance() | ||||
| 				create_stock_in_hand_jv(reverse=cint(self.doc.auto_accounting_for_stock) < previous_val) | ||||
| 	 | ||||
| 	def on_update(self): | ||||
| 		for key in ["auto_accounting_for_stock"]: | ||||
| 			webnotes.conn.set_default(key, self.doc.fields.get(key, '')) | ||||
|  | ||||
| @ -32,6 +32,18 @@ class TestJournalVoucher(unittest.TestCase): | ||||
| 		 | ||||
| 		self.assertTrue(not webnotes.conn.sql("""select name from `tabJournal Voucher Detail` | ||||
| 			where against_jv=%s""", jv_invoice.doc.name)) | ||||
| 	 | ||||
| 	def test_jv_against_stock_account(self): | ||||
| 		webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) | ||||
| 		 | ||||
| 		jv = webnotes.bean(copy=test_records[0]) | ||||
| 		jv.doclist[1].account = "_Test Account Stock in Hand - _TC" | ||||
| 		jv.insert() | ||||
| 		 | ||||
| 		from accounts.general_ledger import StockAccountInvalidTransaction | ||||
| 		self.assertRaises(StockAccountInvalidTransaction, jv.submit) | ||||
| 
 | ||||
| 		webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) | ||||
| 			 | ||||
| 	def test_monthly_budget_crossed_ignore(self): | ||||
| 		webnotes.conn.set_value("Company", "_Test Company", "monthly_bgt_flag", "Ignore") | ||||
|  | ||||
| @ -90,7 +90,6 @@ class DocType(SellingController): | ||||
| 				get_obj('Authorization Control').validate_approving_authority(self.doc.doctype,  | ||||
| 				 	self.doc.company, self.doc.grand_total, self) | ||||
| 				 | ||||
| 		self.set_buying_amount() | ||||
| 		self.check_prev_docstatus() | ||||
| 		 | ||||
| 		self.update_status_updater_args() | ||||
|  | ||||
| @ -330,13 +330,12 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 		 | ||||
| 		self.assertFalse(gle) | ||||
| 		 | ||||
| 	def atest_pos_gl_entry_with_aii(self): | ||||
| 	def test_pos_gl_entry_with_aii(self): | ||||
| 		webnotes.conn.sql("delete from `tabStock Ledger Entry`") | ||||
| 		webnotes.conn.sql("delete from `tabGL Entry`") | ||||
| 		webnotes.conn.sql("delete from `tabBin`") | ||||
| 		webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) | ||||
| 		 | ||||
| 		old_default_company = webnotes.conn.get_default("company") | ||||
| 		webnotes.conn.set_default("company", "_Test Company") | ||||
| 		 | ||||
| 		self._insert_purchase_receipt() | ||||
| 		self._insert_pos_settings() | ||||
| 		 | ||||
| @ -360,20 +359,18 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 			["_Test Item", "_Test Warehouse - _TC", -1.0]) | ||||
| 		 | ||||
| 		# check gl entries | ||||
| 		stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company",  | ||||
| 			"stock_in_hand_account") | ||||
| 		 | ||||
| 		gl_entries = webnotes.conn.sql("""select account, debit, credit | ||||
| 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s | ||||
| 			order by account asc, debit asc""", si.doc.name, as_dict=1) | ||||
| 		self.assertTrue(gl_entries) | ||||
| 		 | ||||
| 				 | ||||
| 		expected_gl_entries = sorted([ | ||||
| 			[si.doc.debit_to, 630.0, 0.0], | ||||
| 			[pos[1]["income_account"], 0.0, 500.0], | ||||
| 			[pos[2]["account_head"], 0.0, 80.0], | ||||
| 			[pos[3]["account_head"], 0.0, 50.0], | ||||
| 			[stock_in_hand_account, 0.0, 75.0], | ||||
| 			["_Test Account Stock In Hand - _TC", 0.0, 75.0], | ||||
| 			[pos[1]["expense_account"], 75.0, 0.0], | ||||
| 			[si.doc.debit_to, 0.0, 600.0], | ||||
| 			["_Test Account Bank Account - _TC", 600.0, 0.0] | ||||
| @ -383,6 +380,8 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 			self.assertEquals(expected_gl_entries[i][1], gle.debit) | ||||
| 			self.assertEquals(expected_gl_entries[i][2], gle.credit) | ||||
| 		 | ||||
| 		 | ||||
| 		 | ||||
| 		# cancel | ||||
| 		si.cancel() | ||||
| 		gle = webnotes.conn.sql("""select * from `tabGL Entry`  | ||||
| @ -390,12 +389,11 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 		 | ||||
| 		self.assertFalse(gle) | ||||
| 		 | ||||
| 		self.assertFalse(get_stock_and_account_difference([si.doclist[1].warehouse])) | ||||
| 		self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"])) | ||||
| 		 | ||||
| 		webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) | ||||
| 		webnotes.conn.set_default("company", old_default_company) | ||||
| 		 | ||||
| 	def atest_sales_invoice_gl_entry_with_aii_no_item_code(self):		 | ||||
| 	def test_sales_invoice_gl_entry_with_aii_no_item_code(self):		 | ||||
| 		webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) | ||||
| 				 | ||||
| 		si_copy = webnotes.copy_doclist(test_records[1]) | ||||
| @ -422,7 +420,7 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 				 | ||||
| 		webnotes.defaults.set_global_default("auto_accounting_for_stock", 0) | ||||
| 	 | ||||
| 	def atest_sales_invoice_gl_entry_with_aii_non_stock_item(self):		 | ||||
| 	def test_sales_invoice_gl_entry_with_aii_non_stock_item(self):		 | ||||
| 		webnotes.defaults.set_global_default("auto_accounting_for_stock", 1) | ||||
| 		 | ||||
| 		si_copy = webnotes.copy_doclist(test_records[1]) | ||||
| @ -641,7 +639,7 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 			return new_si | ||||
| 		 | ||||
| 		# if yearly, test 3 repetitions, else test 13 repetitions | ||||
| 		count = no_of_months == 12 and 3 or 13 | ||||
| 		count = 3 if no_of_months == 12 else 13 | ||||
| 		for i in xrange(count): | ||||
| 			base_si = _test(i) | ||||
| 			 | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|  { | ||||
|   "creation": "2013-06-04 11:02:19",  | ||||
|   "docstatus": 0,  | ||||
|   "modified": "2013-07-25 16:32:10",  | ||||
|   "modified": "2013-08-29 16:58:56",  | ||||
|   "modified_by": "Administrator",  | ||||
|   "owner": "Administrator" | ||||
|  },  | ||||
| @ -416,17 +416,6 @@ | ||||
|   "print_hide": 1,  | ||||
|   "read_only": 1 | ||||
|  },  | ||||
|  { | ||||
|   "doctype": "DocField",  | ||||
|   "fieldname": "buying_amount",  | ||||
|   "fieldtype": "Currency",  | ||||
|   "hidden": 1,  | ||||
|   "label": "Buying Amount",  | ||||
|   "no_copy": 1,  | ||||
|   "options": "Company:company:default_currency",  | ||||
|   "print_hide": 1,  | ||||
|   "read_only": 1 | ||||
|  },  | ||||
|  { | ||||
|   "allow_on_submit": 1,  | ||||
|   "doctype": "DocField",  | ||||
|  | ||||
| @ -5,8 +5,12 @@ from __future__ import unicode_literals | ||||
| import webnotes | ||||
| from webnotes.utils import flt, cstr, now | ||||
| from webnotes.model.doc import Document | ||||
| from webnotes import msgprint, _ | ||||
| from accounts.utils import validate_expense_against_budget | ||||
| 
 | ||||
| 
 | ||||
| class StockAccountInvalidTransaction(webnotes.ValidationError): pass | ||||
| 
 | ||||
| def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True,  | ||||
| 		update_outstanding='Yes'): | ||||
| 	if gl_map: | ||||
| @ -47,8 +51,8 @@ def merge_similar_entries(gl_map): | ||||
| 	merged_gl_map = filter(lambda x: flt(x.debit)!=0 or flt(x.credit)!=0, merged_gl_map) | ||||
| 	return merged_gl_map | ||||
| 
 | ||||
| def check_if_in_list(gle, gl_mqp): | ||||
| 	for e in gl_mqp: | ||||
| def check_if_in_list(gle, gl_map): | ||||
| 	for e in gl_map: | ||||
| 		if e.account == gle.account and \ | ||||
| 				cstr(e.get('against_voucher'))==cstr(gle.get('against_voucher')) \ | ||||
| 				and cstr(e.get('against_voucher_type')) == \ | ||||
| @ -57,11 +61,14 @@ def check_if_in_list(gle, gl_mqp): | ||||
| 			return e | ||||
| 
 | ||||
| def save_entries(gl_map, adv_adj, update_outstanding): | ||||
| 	validate_account_for_auto_accounting_for_stock(gl_map) | ||||
| 	 | ||||
| 	total_debit = total_credit = 0.0 | ||||
| 	for entry in gl_map: | ||||
| 		make_entry(entry, adv_adj, update_outstanding) | ||||
| 		# check against budget | ||||
| 		validate_expense_against_budget(entry) | ||||
| 		 | ||||
| 
 | ||||
| 		# update total debit / credit | ||||
| 		total_debit += flt(entry.debit) | ||||
| @ -79,8 +86,20 @@ def make_entry(args, adv_adj, update_outstanding): | ||||
| 	 | ||||
| def validate_total_debit_credit(total_debit, total_credit): | ||||
| 	if abs(total_debit - total_credit) > 0.005: | ||||
| 		webnotes.throw(webnotes._("Debit and Credit not equal for this voucher: Diff (Debit) is ") + | ||||
| 		webnotes.throw(_("Debit and Credit not equal for this voucher: Diff (Debit) is ") + | ||||
| 		 	cstr(total_debit - total_credit)) | ||||
| 			 | ||||
| def validate_account_for_auto_accounting_for_stock(gl_map): | ||||
| 	if gl_map[0].voucher_type=="Journal Voucher": | ||||
| 		aii_accounts = [d[0] for d in webnotes.conn.sql("""select account from tabWarehouse  | ||||
| 			where ifnull(account, '')!=''""")] | ||||
| 		 | ||||
| 		for entry in gl_map: | ||||
| 			if entry.account in aii_accounts: | ||||
| 				webnotes.throw(_("Account") + ": " + entry.account +  | ||||
| 					_(" can only be debited/credited through Stock transactions"),  | ||||
| 					StockAccountInvalidTransaction) | ||||
| 	 | ||||
| 		 | ||||
| def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,  | ||||
| 		adv_adj=False, update_outstanding="Yes"): | ||||
|  | ||||
| @ -246,79 +246,6 @@ def get_company_default(company, fieldname): | ||||
| 			_("' in Company: ") + company), raise_exception=True) | ||||
| 			 | ||||
| 	return value | ||||
| 		 | ||||
| def create_stock_in_hand_jv(reverse=False): | ||||
| 	from webnotes.utils import nowdate | ||||
| 	today = nowdate() | ||||
| 	fiscal_year = get_fiscal_year(today)[0] | ||||
| 	jv_list = [] | ||||
| 	 | ||||
| 	for company in webnotes.conn.sql_list("select name from `tabCompany`"): | ||||
| 		stock_rbnb_value = get_stock_rbnb_value(company) | ||||
| 		stock_rbnb_value = reverse and -1*stock_rbnb_value or stock_rbnb_value | ||||
| 		if stock_rbnb_value: | ||||
| 			jv = webnotes.bean([ | ||||
| 				{ | ||||
| 					"doctype": "Journal Voucher", | ||||
| 					"naming_series": "JV-AUTO-", | ||||
| 					"company": company, | ||||
| 					"posting_date": today, | ||||
| 					"fiscal_year": fiscal_year, | ||||
| 					"voucher_type": "Journal Entry", | ||||
| 					"user_remark": (_("Perpetual Accounting") + ": " + | ||||
| 						(_("Disabled") if reverse else _("Enabled")) + ". " + | ||||
| 						_("Journal Entry for inventory that is received but not yet invoiced")) | ||||
| 				}, | ||||
| 				{ | ||||
| 					"doctype": "Journal Voucher Detail", | ||||
| 					"parentfield": "entries", | ||||
| 					"account": get_company_default(company, "stock_received_but_not_billed"), | ||||
| 						(stock_rbnb_value > 0 and "credit" or "debit"): abs(stock_rbnb_value) | ||||
| 				}, | ||||
| 				{ | ||||
| 					"doctype": "Journal Voucher Detail", | ||||
| 					"parentfield": "entries", | ||||
| 					"account": get_company_default(company, "stock_adjustment_account"), | ||||
| 						(stock_rbnb_value > 0 and "debit" or "credit"): abs(stock_rbnb_value), | ||||
| 					"cost_center": get_company_default(company, "stock_adjustment_cost_center") | ||||
| 				}, | ||||
| 			]) | ||||
| 			jv.insert() | ||||
| 			 | ||||
| 			jv_list.append(jv.doc.name) | ||||
| 	 | ||||
| 	if jv_list: | ||||
| 		msgprint(_("Following Journal Vouchers have been created automatically") + \ | ||||
| 			":\n%s" % ("\n".join([("<a href=\"#Form/Journal Voucher/%s\">%s</a>" % (jv, jv)) for jv in jv_list]),)) | ||||
| 		 | ||||
| 		msgprint(_("""These adjustment vouchers book the difference between \ | ||||
| 			the total value of received items and the total value of invoiced items, \ | ||||
| 			as a required step to use Perpetual Accounting. | ||||
| 			This is an approximation to get you started. | ||||
| 			You will need to submit these vouchers after checking if the values are correct. | ||||
| 			For more details, read: \ | ||||
| 			<a href="http://erpnext.com/auto-inventory-accounting" target="_blank">\ | ||||
| 			Perpetual Accounting</a>""")) | ||||
| 			 | ||||
| 	webnotes.msgprint("""Please refresh the system to get effect of Perpetual Accounting""") | ||||
| 			 | ||||
| 		 | ||||
| def get_stock_rbnb_value(company): | ||||
| 	total_received_amount = webnotes.conn.sql("""select sum(valuation_rate*qty*conversion_factor)  | ||||
| 		from `tabPurchase Receipt Item` pr_item where docstatus=1  | ||||
| 		and exists(select name from `tabItem` where name = pr_item.item_code  | ||||
| 			and is_stock_item='Yes') | ||||
| 		and exists(select name from `tabPurchase Receipt`  | ||||
| 			where name = pr_item.parent and company = %s)""", company) | ||||
| 		 | ||||
| 	total_billed_amount = webnotes.conn.sql("""select sum(valuation_rate*qty*conversion_factor)  | ||||
| 		from `tabPurchase Invoice Item` pi_item where docstatus=1  | ||||
| 		and exists(select name from `tabItem` where name = pi_item.item_code  | ||||
| 			and is_stock_item='Yes') | ||||
| 		and exists(select name from `tabPurchase Invoice`  | ||||
| 			where name = pi_item.parent and company = %s)""", company) | ||||
| 	return flt(total_received_amount[0][0]) - flt(total_billed_amount[0][0]) | ||||
| 
 | ||||
| 
 | ||||
| def fix_total_debit_credit(): | ||||
| 	vouchers = webnotes.conn.sql("""select voucher_type, voucher_no,  | ||||
| @ -335,14 +262,6 @@ def fix_total_debit_credit(): | ||||
| 				where voucher_type = %s and voucher_no = %s and %s > 0 limit 1""" % | ||||
| 				(dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr),  | ||||
| 				(d.diff, d.voucher_type, d.voucher_no)) | ||||
| 
 | ||||
| def validate_stock_and_account_balance(): | ||||
| 	difference = get_stock_and_account_difference() | ||||
| 	if difference: | ||||
| 		msgprint(_("Account balance must be synced with stock balance, \ | ||||
| 				to enable perpetual accounting." +  | ||||
| 				_(" Following accounts are not synced with stock balance") + ": \n" +  | ||||
| 				"\n".join(difference.keys())), raise_exception=1) | ||||
| 	 | ||||
| def get_stock_and_account_difference(account_list=None, posting_date=None): | ||||
| 	from stock.utils import get_stock_balance_on | ||||
|  | ||||
| @ -83,29 +83,6 @@ class SellingController(StockController): | ||||
| 		if self.meta.get_field("in_words_export"): | ||||
| 			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) | ||||
| 
 | ||||
| 	def set_buying_amount(self, stock_ledger_entries = None): | ||||
| 		from stock.utils import get_buying_amount | ||||
| 		if not stock_ledger_entries: | ||||
| 			stock_ledger_entries = self.get_stock_ledger_entries() | ||||
| 
 | ||||
| 		item_sales_bom = {} | ||||
| 		for d in self.doclist.get({"parentfield": "packing_details"}): | ||||
| 			new_d = webnotes._dict(d.fields.copy()) | ||||
| 			new_d.total_qty = -1 * d.qty | ||||
| 			item_sales_bom.setdefault(d.parent_item, []).append(new_d) | ||||
| 		 | ||||
| 		if stock_ledger_entries: | ||||
| 			stock_items = self.get_stock_items() | ||||
| 			for item in self.doclist.get({"parentfield": self.fname}): | ||||
| 				if item.item_code in stock_items or \ | ||||
| 						(item_sales_bom and item_sales_bom.get(item.item_code)): | ||||
| 					buying_amount = get_buying_amount(item.item_code, self.doc.doctype, self.doc.name, item.name,  | ||||
| 						stock_ledger_entries.get((item.item_code, item.warehouse), []),  | ||||
| 						item_sales_bom) | ||||
| 					item.buying_amount = buying_amount >= 0.01 and buying_amount or 0 | ||||
| 					webnotes.conn.set_value(item.doctype, item.name, "buying_amount",  | ||||
| 						item.buying_amount) | ||||
| 				 | ||||
| 	def calculate_taxes_and_totals(self): | ||||
| 		self.other_fname = "other_charges" | ||||
|  | ||||
| @ -1,33 +0,0 @@ | ||||
| # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. | ||||
| # License: GNU General Public License v3. See license.txt | ||||
| 
 | ||||
| import webnotes | ||||
| from webnotes.utils import now_datetime | ||||
| 
 | ||||
| def execute(): | ||||
| 	webnotes.reload_doc("stock", "doctype", "delivery_note_item") | ||||
| 	webnotes.reload_doc("accounts", "doctype", "sales_invoice_item") | ||||
| 
 | ||||
| 	webnotes.conn.auto_commit_on_many_writes = True | ||||
| 	for company in webnotes.conn.sql("select name from `tabCompany`"): | ||||
| 		stock_ledger_entries = 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 | ||||
| 			order by item_code desc, warehouse desc,  | ||||
| 			posting_date desc, posting_time desc, name desc""", company[0], as_dict=True) | ||||
| 		 | ||||
| 		dn_list = webnotes.conn.sql("""select name from `tabDelivery Note`  | ||||
| 			where docstatus < 2 and company = %s""", company[0]) | ||||
| 		 | ||||
| 		for dn in dn_list: | ||||
| 			dn = webnotes.get_obj("Delivery Note", dn[0], with_children = 1) | ||||
| 			dn.set_buying_amount(stock_ledger_entries) | ||||
| 		 | ||||
| 		si_list = webnotes.conn.sql("""select name from `tabSales Invoice`  | ||||
| 			where docstatus < 2	and company = %s""", company[0]) | ||||
| 		for si in si_list: | ||||
| 			si = webnotes.get_obj("Sales Invoice", si[0], with_children = 1) | ||||
| 			si.set_buying_amount(stock_ledger_entries) | ||||
| 		 | ||||
| 	webnotes.conn.auto_commit_on_many_writes = False | ||||
| @ -186,7 +186,6 @@ class DocType(SellingController): | ||||
| 
 | ||||
| 		self.credit_limit() | ||||
| 		 | ||||
| 		self.set_buying_amount() | ||||
| 		self.make_gl_entries() | ||||
| 
 | ||||
| 		# set DN status | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|  { | ||||
|   "creation": "2013-04-22 13:15:44",  | ||||
|   "docstatus": 0,  | ||||
|   "modified": "2013-08-07 14:45:30",  | ||||
|   "modified": "2013-08-29 16:58:16",  | ||||
|   "modified_by": "Administrator",  | ||||
|   "owner": "Administrator" | ||||
|  },  | ||||
| @ -420,17 +420,6 @@ | ||||
|   "print_hide": 1,  | ||||
|   "read_only": 1 | ||||
|  },  | ||||
|  { | ||||
|   "doctype": "DocField",  | ||||
|   "fieldname": "buying_amount",  | ||||
|   "fieldtype": "Currency",  | ||||
|   "hidden": 1,  | ||||
|   "label": "Buying Amount",  | ||||
|   "no_copy": 1,  | ||||
|   "options": "Company:company:default_currency",  | ||||
|   "print_hide": 1,  | ||||
|   "read_only": 1 | ||||
|  },  | ||||
|  { | ||||
|   "allow_on_submit": 1,  | ||||
|   "doctype": "DocField",  | ||||
|  | ||||
| @ -7,10 +7,7 @@ from webnotes.utils import cint, cstr, flt | ||||
| from webnotes.model.doc import addchild | ||||
| from webnotes.model.bean import getlist | ||||
| from webnotes.model.code import get_obj | ||||
| from webnotes import msgprint | ||||
| 
 | ||||
| sql = webnotes.conn.sql | ||||
| 
 | ||||
| from webnotes import msgprint, _ | ||||
| 
 | ||||
| class DocType: | ||||
| 	def __init__(self, doc, doclist=[]): | ||||
| @ -18,47 +15,66 @@ class DocType: | ||||
| 		self.doclist = doclist | ||||
| 		self.prwise_cost = {} | ||||
| 		 | ||||
| 		 | ||||
| 	def check_mandatory(self): | ||||
| 		""" Check mandatory fields """		 | ||||
| 		if not self.doc.from_pr_date or not self.doc.to_pr_date: | ||||
| 			msgprint("Please enter From and To PR Date", raise_exception=1) | ||||
| 			webnotes.throw(_("Please enter From and To PR Date")) | ||||
| 
 | ||||
| 		if not self.doc.currency: | ||||
| 			msgprint("Please enter Currency.", raise_exception=1) | ||||
| 
 | ||||
| 			webnotes.throw(_("Please enter Currency")) | ||||
| 			 | ||||
| 	def update_landed_cost(self): | ||||
| 		"""  | ||||
| 			Add extra cost and recalculate all values in pr,  | ||||
| 			Recalculate valuation rate in all sle after pr posting date | ||||
| 		"""	 | ||||
| 		self.get_selected_pr() | ||||
| 		self.validate_selected_pr()			 | ||||
| 		self.add_charges_in_pr()		 | ||||
| 		self.cal_charges_and_item_tax_amt() | ||||
| 		self.update_sle() | ||||
| 		msgprint("Landed Cost updated successfully") | ||||
| 		 | ||||
| 	def get_selected_pr(self): | ||||
| 		""" Get selected purchase receipt no """ | ||||
| 		self.selected_pr = [d.purchase_receipt for d in \ | ||||
| 			self.doclist.get({"parentfield": "lc_pr_details"}) if d.select_pr] | ||||
| 		if not self.selected_pr: | ||||
| 			webnotes.throw(_("Please select atleast one PR to proceed.")) | ||||
| 
 | ||||
| 	def get_purchase_receipts(self): | ||||
| 		"""	Get purchase receipts for given period """ | ||||
| 				 | ||||
| 		self.doclist = self.doc.clear_table(self.doclist,'lc_pr_details',1) | ||||
| 		self.doclist = self.doc.clear_table(self.doclist,'lc_pr_details') | ||||
| 		self.check_mandatory() | ||||
| 		 | ||||
| 		pr = sql("select name from `tabPurchase Receipt` where docstatus = 1 and posting_date >= '%s' and posting_date <= '%s' and currency = '%s' order by name " % (self.doc.from_pr_date, self.doc.to_pr_date, self.doc.currency), as_dict = 1) | ||||
| 		if len(pr)>200: | ||||
| 			msgprint("Please enter date of shorter duration as there are too many purchase receipt, hence it cannot be loaded.", raise_exception=1) | ||||
| 		pr = webnotes.conn.sql("""select name from `tabPurchase Receipt` where docstatus = 1  | ||||
| 			and posting_date>=%s and posting_date<=%s and currency=%s order by name """,  | ||||
| 			(self.doc.from_pr_date, self.doc.to_pr_date, self.doc.currency), as_dict = 1) | ||||
| 		if len(pr) > 200: | ||||
| 			webnotes.throw(_("Please enter date of shorter duration as there are too many \ | ||||
| 				purchase receipt, hence it cannot be loaded.")) | ||||
| 			 | ||||
| 		for i in pr: | ||||
| 			ch = addchild(self.doc, 'lc_pr_details', 'Landed Cost Purchase Receipt',  | ||||
| 				self.doclist) | ||||
| 			ch.purchase_receipt = i and i['name'] or '' | ||||
| 			ch.save() | ||||
| 
 | ||||
| 	def get_selected_pr(self): | ||||
| 		""" Get selected purchase receipt no """ | ||||
| 		self.selected_pr = [d.purchase_receipt for d in getlist(self.doclist, 'lc_pr_details') if d.select_pr] | ||||
| 		if not self.selected_pr: | ||||
| 			msgprint("Please select atleast one PR to proceed.", raise_exception=1) | ||||
| 			ch.purchase_receipt = i.name | ||||
| 		 | ||||
| 	def validate_selected_pr(self): | ||||
| 		"""Validate selected PR as submitted""" | ||||
| 		invalid_pr =  sql("SELECT name FROM `tabPurchase Receipt` WHERE docstatus != 1 and name in (%s)" % ("'" + "', '".join(self.selected_pr) + "'")) | ||||
| 		invalid_pr =  webnotes.conn.sql("""SELECT name FROM `tabPurchase Receipt`  | ||||
| 			WHERE docstatus!=1 and name in (%s)""" %  | ||||
| 			', '.join(['%s']*len(self.selected_pr)), tuple(self.selected_pr)) | ||||
| 		if invalid_pr: | ||||
| 			msgprint("Selected purchase receipts must be submitted. Following PR are not submitted: %s" % invalid_pr, raise_exception=1) | ||||
| 			webnotes.throw(_("Selected purchase receipts must be submitted. \ | ||||
| 				Following PR are not submitted") + ": " + invalid_pr) | ||||
| 			 | ||||
| 
 | ||||
| 	def get_total_amt(self): | ||||
| 		""" Get sum of net total of all selected PR"""		 | ||||
| 		return sql("SELECT SUM(net_total) FROM `tabPurchase Receipt` WHERE name in (%s)" % ("'" + "', '".join(self.selected_pr) + "'"))[0][0] | ||||
| 		return webnotes.conn.sql("""SELECT SUM(net_total) FROM `tabPurchase Receipt`  | ||||
| 			WHERE name in (%s)""" % ', '.join(['%s']*len(self.selected_pr)),  | ||||
| 			tuple(self.selected_pr))[0][0] | ||||
| 		 | ||||
| 
 | ||||
| 	def add_charges_in_pr(self): | ||||
| @ -74,7 +90,9 @@ class DocType: | ||||
| 				self.prwise_cost[pr] = self.prwise_cost.get(pr, 0) + amt | ||||
| 				cumulative_grand_total += amt | ||||
| 				 | ||||
| 				pr_oc_row = sql("select name from `tabPurchase Taxes and Charges` where parent = %s and category = 'Valuation' and add_deduct_tax = 'Add' and charge_type = 'Actual' and account_head = %s",(pr, lc.account_head)) | ||||
| 				pr_oc_row = webnotes.conn.sql("""select name from `tabPurchase Taxes and Charges`  | ||||
| 						where parent = %s and category = 'Valuation' and add_deduct_tax = 'Add'  | ||||
| 						and charge_type = 'Actual' and account_head = %s""",(pr, lc.account_head)) | ||||
| 				if not pr_oc_row:	# add if not exists | ||||
| 					ch = addchild(pr_obj.doc, 'purchase_tax_details', 'Purchase Taxes and Charges') | ||||
| 					ch.category = 'Valuation' | ||||
| @ -89,7 +107,9 @@ class DocType: | ||||
| 					ch.idx = 500 # add at the end | ||||
| 					ch.save(1) | ||||
| 				else:	# overwrite if exists | ||||
| 					sql("update `tabPurchase Taxes and Charges` set rate = %s, tax_amount = %s where name = %s and parent = %s ", (amt, amt, pr_oc_row[0][0], pr)) | ||||
| 					webnotes.conn.sql("""update `tabPurchase Taxes and Charges`  | ||||
| 						set rate = %s, tax_amount = %s where name = %s and parent = %s""",  | ||||
| 						(amt, amt, pr_oc_row[0][0], pr)) | ||||
| 		 | ||||
| 		 | ||||
| 	def reset_other_charges(self, pr_obj): | ||||
| @ -201,9 +221,9 @@ class DocType: | ||||
| 					d.save() | ||||
| 					if d.serial_no: | ||||
| 						self.update_serial_no(d.serial_no, d.valuation_rate) | ||||
| 				sql("update `tabStock Ledger Entry` set incoming_rate = '%s' where voucher_detail_no = '%s'"%(flt(d.valuation_rate), d.name)) | ||||
| 				webnotes.conn.sql("update `tabStock Ledger Entry` set incoming_rate = '%s' where voucher_detail_no = '%s'"%(flt(d.valuation_rate), d.name)) | ||||
| 				 | ||||
| 				res = sql("""select item_code, warehouse, posting_date, posting_time  | ||||
| 				res = webnotes.conn.sql("""select item_code, warehouse, posting_date, posting_time  | ||||
| 					from `tabStock Ledger Entry` where voucher_detail_no = %s LIMIT 1""",  | ||||
| 					d.name, as_dict=1) | ||||
| 
 | ||||
| @ -211,22 +231,9 @@ class DocType: | ||||
| 				if res: | ||||
| 					update_entries_after(res[0]) | ||||
| 
 | ||||
| 	 | ||||
| 	def update_serial_no(self, sr_no, rate): | ||||
| 		""" update valuation rate in serial no""" | ||||
| 		sr_no = map(lambda x: x.strip(), cstr(sr_no).split('\n')) | ||||
| 		 | ||||
| 		webnotes.conn.sql("""update `tabSerial No` set purchase_rate = %s where name in (%s)""" %  | ||||
| 			('%s', ', '.join(['%s']*len(sr_no))), tuple([rate] + sr_no)) | ||||
| 				 | ||||
| 	def update_landed_cost(self): | ||||
| 		"""  | ||||
| 			Add extra cost and recalculate all values in pr,  | ||||
| 			Recalculate valuation rate in all sle after pr posting date | ||||
| 		"""	 | ||||
| 		self.get_selected_pr() | ||||
| 		self.validate_selected_pr()			 | ||||
| 		self.add_charges_in_pr()		 | ||||
| 		self.cal_charges_and_item_tax_amt() | ||||
| 		self.update_sle() | ||||
| 		msgprint("Landed Cost updated successfully") | ||||
| 			('%s', ', '.join(['%s']*len(sr_no))), tuple([rate] + sr_no)) | ||||
| @ -39,9 +39,9 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ | ||||
| 		}; | ||||
| 		 | ||||
| 		if(cint(wn.defaults.get_default("auto_accounting_for_stock"))) { | ||||
| 			this.frm.add_fetch("company", "stock_adjustment_account", "expense_adjustment_account"); | ||||
| 
 | ||||
| 			this.frm.fields_dict["expense_adjustment_account"].get_query = function() { | ||||
| 			this.frm.add_fetch("company", "stock_adjustment_account", "expense_account"); | ||||
| 			this.frm.fields_dict.mtn_details.grid.get_field('expense_account').get_query =  | ||||
| 					function() { | ||||
| 				return { | ||||
| 					filters: {  | ||||
| 						"company": me.frm.doc.company, | ||||
| @ -88,7 +88,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ | ||||
| 	set_default_account: function() { | ||||
| 		var me = this; | ||||
| 		 | ||||
| 		if (cint(wn.defaults.get_default("auto_inventory_accounting")) && !this.frm.doc.expense_adjustment_account) { | ||||
| 		if(cint(wn.defaults.get_default("auto_accounting_for_stock")) { | ||||
| 			var account_for = "stock_adjustment_account"; | ||||
| 			if (this.frm.doc.purpose == "Sales Return") | ||||
| 				account_for = "stock_in_hand_account"; | ||||
| @ -102,12 +102,22 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ | ||||
| 					"company": this.frm.doc.company | ||||
| 				}, | ||||
| 				callback: function(r) { | ||||
| 					if (!r.exc) me.frm.set_value("expense_adjustment_account", r.message); | ||||
| 					if (!r.exc) { | ||||
| 						for(d in getchildren('Stock Entry Detail',doc.name,'mtn_details')) { | ||||
| 							if(!d.expense_account) d.expense_account = r.message; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			}); | ||||
| 		} | ||||
| 	}, | ||||
| 	 | ||||
| 	entries_add: function(doc, cdt, cdn) { | ||||
| 		var row = wn.model.get_doc(cdt, cdn); | ||||
| 		this.frm.script_manager.copy_from_first_row("mtn_details", row,  | ||||
| 			["expense_account", "cost_center"]); | ||||
| 	}, | ||||
| 	 | ||||
| 	clean_up: function() { | ||||
| 		// Clear Production Order record from locals, because it is updated via Stock Entry
 | ||||
| 		if(this.frm.doc.production_order &&  | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|  { | ||||
|   "creation": "2013-03-29 18:22:12",  | ||||
|   "docstatus": 0,  | ||||
|   "modified": "2013-08-28 19:15:55",  | ||||
|   "modified": "2013-08-28 19:25:38",  | ||||
|   "modified_by": "Administrator",  | ||||
|   "owner": "Administrator" | ||||
|  },  | ||||
| @ -149,7 +149,7 @@ | ||||
|   "doctype": "DocField",  | ||||
|   "fieldname": "expense_account",  | ||||
|   "fieldtype": "Link",  | ||||
|   "label": "Expense/Adjustment Account",  | ||||
|   "label": "Difference Account",  | ||||
|   "options": "Account",  | ||||
|   "print_hide": 1 | ||||
|  },  | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|  { | ||||
|   "creation": "2013-03-28 10:35:31",  | ||||
|   "docstatus": 0,  | ||||
|   "modified": "2013-08-07 18:16:18",  | ||||
|   "modified": "2013-08-29 16:46:33",  | ||||
|   "modified_by": "Administrator",  | ||||
|   "owner": "Administrator" | ||||
|  },  | ||||
| @ -102,11 +102,11 @@ | ||||
|   "reqd": 1 | ||||
|  },  | ||||
|  { | ||||
|   "depends_on": "eval:sys_defaults.auto_inventory_accounting",  | ||||
|   "depends_on": "eval:sys_defaults.auto_accounting_for_stock",  | ||||
|   "doctype": "DocField",  | ||||
|   "fieldname": "expense_account",  | ||||
|   "fieldtype": "Link",  | ||||
|   "label": "Expense Account",  | ||||
|   "label": "Difference Account",  | ||||
|   "options": "Account" | ||||
|  },  | ||||
|  { | ||||
|  | ||||
| @ -22,7 +22,6 @@ cur_frm.set_query("account", function() { | ||||
| 		filters: { | ||||
| 			"company": cur_frm.doc.company, | ||||
| 			"debit_or_credit": "Debit", | ||||
| 			"is_pl_account": "No", | ||||
| 			'group_or_ledger': "Ledger" | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
| from __future__ import unicode_literals | ||||
| import webnotes | ||||
| 
 | ||||
| from webnotes.utils import flt, validate_email_add | ||||
| from webnotes.utils import cint, flt, validate_email_add | ||||
| from webnotes.model.code import get_obj | ||||
| from webnotes import msgprint | ||||
| 
 | ||||
| @ -23,6 +23,21 @@ class DocType: | ||||
| 	def validate(self): | ||||
| 		if self.doc.email_id and not validate_email_add(self.doc.email_id): | ||||
| 				msgprint("Please enter valid Email Id", raise_exception=1) | ||||
| 				 | ||||
| 		self.account_mandatory() | ||||
| 		 | ||||
| 	def account_mandatory(self): | ||||
| 		if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")): | ||||
| 			sle_exists = webnotes.conn.get_value("Stock Ledger Entry", {"warehouse": self.doc.name}) | ||||
| 			if not self.doc.account and (self.doc.__islocal or not sle_exists): | ||||
| 				webnotes.throw(_("Asset/Expense Account mandatory")) | ||||
| 				 | ||||
| 			if not self.doc.__islocal and sle_exists: | ||||
| 				old_account = webnotes.conn.get_value("Warehouse", self.doc.name, "account") | ||||
| 				if old_account != self.doc.account: | ||||
| 					webnotes.throw(_("Account can not be changed/assigned/removed as \ | ||||
| 						stock transactions exist for this warehouse")) | ||||
| 						 | ||||
| 
 | ||||
| 	def merge_warehouses(self): | ||||
| 		webnotes.conn.auto_commit_on_many_writes = 1 | ||||
|  | ||||
| @ -82,7 +82,7 @@ def update_entries_after(args, verbose=1): | ||||
| 
 | ||||
| 	valuation_method = get_valuation_method(args["item_code"]) | ||||
| 	stock_value_difference = 0.0 | ||||
| 	 | ||||
| 
 | ||||
| 	for sle in entries_to_fix: | ||||
| 		if sle.serial_no or not cint(webnotes.conn.get_default("allow_negative_stock")): | ||||
| 			# validate negative stock for serialized items, fifo valuation  | ||||
| @ -90,7 +90,7 @@ def update_entries_after(args, verbose=1): | ||||
| 			if not validate_negative_stock(qty_after_transaction, sle): | ||||
| 				qty_after_transaction += flt(sle.actual_qty) | ||||
| 				continue | ||||
| 				 | ||||
| 
 | ||||
| 		if sle.serial_no: | ||||
| 			valuation_rate = get_serialized_values(qty_after_transaction, sle, valuation_rate) | ||||
| 		elif valuation_method == "Moving Average": | ||||
| @ -172,6 +172,7 @@ def get_stock_ledger_entries(args, conditions=None, order="desc", limit=None, fo | ||||
| 	return webnotes.conn.sql("""select * from `tabStock Ledger Entry` | ||||
| 		where item_code = %%(item_code)s | ||||
| 		and warehouse = %%(warehouse)s | ||||
| 		and ifnull(is_cancelled, 'No')='No' | ||||
| 		%(conditions)s | ||||
| 		order by timestamp(posting_date, posting_time) %(order)s, name %(order)s | ||||
| 		%(limit)s %(for_update)s""" % { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user