From bb777560691c51634ec36ecdf79e75e047d74e9a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 29 Aug 2013 18:19:37 +0530 Subject: [PATCH] [fix] [minor] auto accounting for stock transactions --- .../accounts_settings/accounts_settings.py | 13 --- .../journal_voucher/test_journal_voucher.py | 12 +++ .../doctype/sales_invoice/sales_invoice.py | 1 - .../sales_invoice/test_sales_invoice.py | 24 +++-- .../sales_invoice_item/sales_invoice_item.txt | 13 +-- accounts/general_ledger.py | 25 +++++- accounts/utils.py | 81 ----------------- controllers/selling_controller.py | 23 ----- .../march_2013/p03_update_buying_amount.py | 33 ------- stock/doctype/delivery_note/delivery_note.py | 1 - .../delivery_note_item/delivery_note_item.txt | 13 +-- .../landed_cost_wizard/landed_cost_wizard.py | 89 ++++++++++--------- stock/doctype/stock_entry/stock_entry.js | 20 +++-- .../stock_entry_detail/stock_entry_detail.txt | 4 +- .../stock_reconciliation.txt | 6 +- stock/doctype/warehouse/warehouse.js | 1 - stock/doctype/warehouse/warehouse.py | 17 +++- stock/stock_ledger.py | 5 +- 18 files changed, 134 insertions(+), 247 deletions(-) delete mode 100644 patches/march_2013/p03_update_buying_amount.py diff --git a/accounts/doctype/accounts_settings/accounts_settings.py b/accounts/doctype/accounts_settings/accounts_settings.py index 5f0d276fb7..b18f14d994 100644 --- a/accounts/doctype/accounts_settings/accounts_settings.py +++ b/accounts/doctype/accounts_settings/accounts_settings.py @@ -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, '')) diff --git a/accounts/doctype/journal_voucher/test_journal_voucher.py b/accounts/doctype/journal_voucher/test_journal_voucher.py index 6c24c915ba..6ade931fdd 100644 --- a/accounts/doctype/journal_voucher/test_journal_voucher.py +++ b/accounts/doctype/journal_voucher/test_journal_voucher.py @@ -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") diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index b31eda5fb7..fe2ed63f24 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -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() diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index 4f82efca65..c2b9c8f071 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -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) diff --git a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt index 057f166d75..07cdc547e0 100644 --- a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt +++ b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt @@ -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", diff --git a/accounts/general_ledger.py b/accounts/general_ledger.py index 876a581955..acb1694d7b 100644 --- a/accounts/general_ledger.py +++ b/accounts/general_ledger.py @@ -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"): diff --git a/accounts/utils.py b/accounts/utils.py index e88804abeb..2e28254c5e 100644 --- a/accounts/utils.py +++ b/accounts/utils.py @@ -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([("%s" % (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: \ - \ - Perpetual Accounting""")) - - 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 diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 2cec4ea382..4fa97fc093 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -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" diff --git a/patches/march_2013/p03_update_buying_amount.py b/patches/march_2013/p03_update_buying_amount.py deleted file mode 100644 index ddb52e0277..0000000000 --- a/patches/march_2013/p03_update_buying_amount.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index b07ba5a379..b0b2e5dd31 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -186,7 +186,6 @@ class DocType(SellingController): self.credit_limit() - self.set_buying_amount() self.make_gl_entries() # set DN status diff --git a/stock/doctype/delivery_note_item/delivery_note_item.txt b/stock/doctype/delivery_note_item/delivery_note_item.txt index 83b8f75c9c..b74c33a621 100644 --- a/stock/doctype/delivery_note_item/delivery_note_item.txt +++ b/stock/doctype/delivery_note_item/delivery_note_item.txt @@ -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", diff --git a/stock/doctype/landed_cost_wizard/landed_cost_wizard.py b/stock/doctype/landed_cost_wizard/landed_cost_wizard.py index 0cdad4d949..f46c0c2121 100644 --- a/stock/doctype/landed_cost_wizard/landed_cost_wizard.py +++ b/stock/doctype/landed_cost_wizard/landed_cost_wizard.py @@ -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)) \ No newline at end of file diff --git a/stock/doctype/stock_entry/stock_entry.js b/stock/doctype/stock_entry/stock_entry.js index d8983d5c12..87f9a9c39f 100644 --- a/stock/doctype/stock_entry/stock_entry.js +++ b/stock/doctype/stock_entry/stock_entry.js @@ -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 && diff --git a/stock/doctype/stock_entry_detail/stock_entry_detail.txt b/stock/doctype/stock_entry_detail/stock_entry_detail.txt index 051eb7c6e6..b400cdd248 100644 --- a/stock/doctype/stock_entry_detail/stock_entry_detail.txt +++ b/stock/doctype/stock_entry_detail/stock_entry_detail.txt @@ -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 }, diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.txt b/stock/doctype/stock_reconciliation/stock_reconciliation.txt index 2891ad260a..e5b1b7420f 100644 --- a/stock/doctype/stock_reconciliation/stock_reconciliation.txt +++ b/stock/doctype/stock_reconciliation/stock_reconciliation.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-28 10:35:31", "docstatus": 0, - "modified": "2013-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" }, { diff --git a/stock/doctype/warehouse/warehouse.js b/stock/doctype/warehouse/warehouse.js index 3451863a60..34745f5346 100644 --- a/stock/doctype/warehouse/warehouse.js +++ b/stock/doctype/warehouse/warehouse.js @@ -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" } } diff --git a/stock/doctype/warehouse/warehouse.py b/stock/doctype/warehouse/warehouse.py index bed314c63a..6a532ffaf0 100644 --- a/stock/doctype/warehouse/warehouse.py +++ b/stock/doctype/warehouse/warehouse.py @@ -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 diff --git a/stock/stock_ledger.py b/stock/stock_ledger.py index a4e8d265a8..a3af5ade81 100644 --- a/stock/stock_ledger.py +++ b/stock/stock_ledger.py @@ -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""" % {