From 58b2a101aee16bddef75f704042e81dedc0abd71 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 7 Mar 2013 11:42:14 +0530 Subject: [PATCH 1/4] aii: testcases for sales invoice gl entries --- .../doctype/sales_invoice/sales_invoice.py | 2 +- .../sales_invoice/test_sales_invoice.py | 237 ++++++++++++++---- .../delivery_note/test_delivery_note.py | 2 +- 3 files changed, 184 insertions(+), 57 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 47e3195d71..b31d54919c 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -8,7 +8,7 @@ # # 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 +# 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 diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index fb290d27e3..1f165f0c33 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -38,72 +38,199 @@ class TestSalesInvoice(unittest.TestCase): si.insert() si.submit() - self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", "status"), - "Billed") + self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", + "status"), "Billed") self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Billed") si.cancel() - self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", "status"), - "Submitted") + self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", + "status"), "Submitted") self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Batched for Billing") + def test_sales_invoice_gl_entry_without_aii(self): + si = webnotes.bean(webnotes.copy_doclist(test_records[1])) + si.insert() + si.submit() + webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + + 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""", si.doc.name, as_dict=1) + self.assertTrue(gl_entries) + expected_values = sorted([ + [si.doc.debit_to, 630.0, 0.0], + [test_records[1][1]["income_account"], 0.0, 500.0], + [test_records[1][2]["account_head"], 0.0, 80.0], + [test_records[1][3]["account_head"], 0.0, 50.0], + ]) + + for i, gle in enumerate(gl_entries): + self.assertEquals(expected_values[i][0], gle.account) + self.assertEquals(expected_values[i][1], gle.debit) + self.assertEquals(expected_values[i][2], gle.credit) + + def test_sales_invoice_gl_entry_with_aii(self): + webnotes.defaults.set_global_default("auto_inventory_accounting", 1) + + self._insert_purchase_receipt() + dn = self._insert_delivery_note() + + si_against_dn = webnotes.copy_doclist(test_records[1]) + si_against_dn[1]["delivery_note"] = dn.doc.name + si = webnotes.bean(si_against_dn) + si.insert() + si.submit() + + 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""", si.doc.name, as_dict=1) + self.assertTrue(gl_entries) + + expected_values = sorted([ + [si.doc.debit_to, 630.0, 0.0], + [test_records[1][1]["income_account"], 0.0, 500.0], + [test_records[1][2]["account_head"], 0.0, 80.0], + [test_records[1][3]["account_head"], 0.0, 50.0], + ["Stock Delivered But Not Billed - _TC", 0.0, 375.0], + [test_records[1][1]["expense_account"], 375.0, 0.0] + ]) + print expected_values + print gl_entries + for i, gle in enumerate(gl_entries): + self.assertEquals(expected_values[i][0], gle.account) + self.assertEquals(expected_values[i][1], gle.debit) + self.assertEquals(expected_values[i][2], gle.credit) + + webnotes.defaults.set_global_default("auto_inventory_accounting", 1) + + + def _insert_purchase_receipt(self): + from stock.doctype.purchase_receipt.test_purchase_receipt import test_records \ + as pr_test_records + pr = webnotes.bean(copy=pr_test_records[0]) + pr.run_method("calculate_taxes_and_totals") + pr.insert() + pr.submit() + + def _insert_delivery_note(self): + from stock.doctype.delivery_note.test_delivery_note import test_records \ + as dn_test_records + dn = webnotes.bean(copy=dn_test_records[0]) + dn.insert() + dn.submit() + return dn test_dependencies = ["Journal Voucher"] -test_records = [[ - { - "naming_series": "_T-Sales Invoice-", - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "debit_to": "_Test Customer - _TC", - "customer": "_Test Customer", - "customer_name": "_Test Customer", - "doctype": "Sales Invoice", - "due_date": "2013-01-23", - "fiscal_year": "_Test Fiscal Year 2013", - "grand_total": 561.8, - "grand_total_export": 561.8, - "net_total": 500.0, - "plc_conversion_rate": 1.0, - "posting_date": "2013-01-23", - "price_list_currency": "INR", - "price_list_name": "_Test Price List", - "territory": "_Test Territory" - }, - { - "amount": 500.0, - "basic_rate": 500.0, - "description": "138-CMS Shoe", - "doctype": "Sales Invoice Item", - "export_amount": 500.0, - "export_rate": 500.0, - "income_account": "Sales - _TC", - "cost_center": "_Test Cost Center - _TC", - "item_name": "138-CMS Shoe", - "parentfield": "entries", - "qty": 1.0 - }, - { - "account_head": "_Test Account VAT - _TC", - "charge_type": "On Net Total", - "description": "VAT", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "tax_amount": 30.0, - }, - { - "account_head": "_Test Account Service Tax - _TC", - "charge_type": "On Net Total", - "description": "Service Tax", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "tax_amount": 31.8, - } -]] \ No newline at end of file +test_records = [ + [ + { + "naming_series": "_T-Sales Invoice-", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "debit_to": "_Test Customer - _TC", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total": 561.8, + "grand_total_export": 561.8, + "net_total": 500.0, + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", + "price_list_name": "_Test Price List", + "territory": "_Test Territory" + }, + { + "amount": 500.0, + "basic_rate": 500.0, + "description": "138-CMS Shoe", + "doctype": "Sales Invoice Item", + "export_amount": 500.0, + "export_rate": 500.0, + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + "item_name": "138-CMS Shoe", + "parentfield": "entries", + "qty": 1.0 + }, + { + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "tax_amount": 30.0, + }, + { + "account_head": "_Test Account Service Tax - _TC", + "charge_type": "On Net Total", + "description": "Service Tax", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "tax_amount": 31.8, + } + ], + [ + { + "naming_series": "_T-Sales Invoice-", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "debit_to": "_Test Customer - _TC", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total": 630.0, + "grand_total_export": 630.0, + "net_total": 500.0, + "plc_conversion_rate": 1.0, + "posting_date": "2013-03-07", + "price_list_currency": "INR", + "price_list_name": "_Test Price List", + "territory": "_Test Territory" + }, + { + "item_code": "_Test Item", + "item_name": "_Test Item", + "description": "_Test Item", + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "qty": 1.0, + "basic_rate": 500.0, + "amount": 500.0, + "export_rate": 500.0, + "export_amount": 500.0, + "income_account": "Sales - _TC", + "expense_account": "_Test Account Cost for Goods Sold", + "cost_center": "_Test Cost Center - _TC", + }, + { + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "tax_amount": 80.0, + }, + { + "account_head": "_Test Account Service Tax - _TC", + "charge_type": "On Net Total", + "description": "Service Tax", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "tax_amount": 50.0, + } + ], +] \ No newline at end of file diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py index c2bb5d07ca..acdf8b9e07 100644 --- a/stock/doctype/delivery_note/test_delivery_note.py +++ b/stock/doctype/delivery_note/test_delivery_note.py @@ -109,7 +109,7 @@ test_records = [ "description": "CPU", "doctype": "Delivery Note Item", "item_code": "_Test Item", - "item_name": "CPU", + "item_name": "_Test Item", "parentfield": "delivery_note_details", "qty": 5.0, "basic_rate": 100.0, From b96fef980238cb96b8760170e4b15dd9f2235f68 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 7 Mar 2013 15:35:36 +0530 Subject: [PATCH 2/4] remove cancelled entries from grid report when fetching data based on modified --- accounts/general_ledger.py | 8 +++++--- .../financial_analytics/financial_analytics.js | 8 +++++--- accounts/page/general_ledger/general_ledger.js | 2 +- .../page/purchase_analytics/purchase_analytics.js | 14 ++------------ selling/page/sales_analytics/sales_analytics.js | 12 +----------- 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/accounts/general_ledger.py b/accounts/general_ledger.py index 215c351421..8e0f4082bc 100644 --- a/accounts/general_ledger.py +++ b/accounts/general_ledger.py @@ -16,7 +16,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import flt, cstr +from webnotes.utils import flt, cstr, now from webnotes.model.doc import Document def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, @@ -109,5 +109,7 @@ def validate_total_debit_credit(total_debit, total_credit): (total_debit - total_credit), raise_exception=1) def set_as_cancel(voucher_type, voucher_no): - webnotes.conn.sql("""update `tabGL Entry` set is_cancelled='Yes' - where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) \ No newline at end of file + webnotes.conn.sql("""update `tabGL Entry` set is_cancelled='Yes', + modified=%s, modified_by=%s + where voucher_type=%s and voucher_no=%s""", + (now(), webnotes.session.user, voucher_type, voucher_no)) \ No newline at end of file diff --git a/accounts/page/financial_analytics/financial_analytics.js b/accounts/page/financial_analytics/financial_analytics.js index f0bafdb07b..f7145490a1 100644 --- a/accounts/page/financial_analytics/financial_analytics.js +++ b/accounts/page/financial_analytics/financial_analytics.js @@ -71,9 +71,11 @@ erpnext.FinancialAnalytics = erpnext.AccountTreeGrid.extend({ setup_filters: function() { var me = this; this._super(); - this.filter_inputs.pl_or_bs.change(function() { - me.filter_inputs.refresh.click(); - }).add_options($.map(wn.report_dump.data["Cost Center"], function(v) {return v.name;})); + this.trigger_refresh_on_change(["pl_or_bs"]); + + this.filter_inputs.pl_or_bs + .add_options($.map(wn.report_dump.data["Cost Center"], function(v) {return v.name;})); + this.setup_plot_check(); }, init_filter_values: function() { diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js index 8f6b598d56..4a3f21ea92 100644 --- a/accounts/page/general_ledger/general_ledger.js +++ b/accounts/page/general_ledger/general_ledger.js @@ -108,7 +108,7 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ // filter accounts options by company this.filter_inputs.company.change(function() { me.setup_account_filter(this); - me.filter_inputs.refresh.click(); + me.set_route() }); this.filter_inputs.account.change(function() { diff --git a/buying/page/purchase_analytics/purchase_analytics.js b/buying/page/purchase_analytics/purchase_analytics.js index 7d8171ebbd..fc082eae13 100644 --- a/buying/page/purchase_analytics/purchase_analytics.js +++ b/buying/page/purchase_analytics/purchase_analytics.js @@ -120,19 +120,9 @@ erpnext.PurchaseAnalytics = wn.views.TreeGridReport.extend({ setup_filters: function() { var me = this; this._super(); - - this.filter_inputs.value_or_qty.change(function() { - me.filter_inputs.refresh.click(); - }); - - this.filter_inputs.tree_type.change(function() { - me.filter_inputs.refresh.click(); - }); - - this.filter_inputs.based_on.change(function() { - me.filter_inputs.refresh.click(); - }); + this.trigger_refresh_on_change(["value_or_qty", "tree_type", "based_on"]); + this.show_zero_check() this.setup_plot_check(); }, diff --git a/selling/page/sales_analytics/sales_analytics.js b/selling/page/sales_analytics/sales_analytics.js index 499c6c0156..0b35af5f31 100644 --- a/selling/page/sales_analytics/sales_analytics.js +++ b/selling/page/sales_analytics/sales_analytics.js @@ -122,18 +122,8 @@ erpnext.SalesAnalytics = wn.views.TreeGridReport.extend({ setup_filters: function() { var me = this; this._super(); - - this.filter_inputs.value_or_qty.change(function() { - me.filter_inputs.refresh.click(); - }); - - this.filter_inputs.tree_type.change(function() { - me.filter_inputs.refresh.click(); - }); - this.filter_inputs.based_on.change(function() { - me.filter_inputs.refresh.click(); - }); + this.trigger_refresh_on_change(["value_or_qty", "tree_type", "based_on"]); this.show_zero_check() this.setup_plot_check(); From f1e274c8b47d34bdcb70631491a2a5830faa485d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 7 Mar 2013 16:34:59 +0530 Subject: [PATCH 3/4] when cancelling stock ledger entry, change modified and modified by --- stock/doctype/stock_ledger/stock_ledger.py | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py index 05fc0e0642..5dff992ae4 100644 --- a/stock/doctype/stock_ledger/stock_ledger.py +++ b/stock/doctype/stock_ledger/stock_ledger.py @@ -17,7 +17,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import add_days, cstr, flt, nowdate, cint +from webnotes.utils import add_days, cstr, flt, nowdate, cint, now from webnotes.model.doc import Document from webnotes.model.bean import getlist from webnotes.model.code import get_obj @@ -49,7 +49,7 @@ class DocType: serial_nos = get_valid_serial_nos(d.serial_no) for s in serial_nos: s = s.strip() - sr_war = sql("select warehouse,name from `tabSerial No` where name = '%s'" % (s)) + sr_war = webnotes.conn.sql("select warehouse,name from `tabSerial No` where name = '%s'" % (s)) if not sr_war: msgprint("Serial No %s does not exists"%s, raise_exception = 1) elif not sr_war[0][0]: @@ -81,7 +81,7 @@ class DocType: def set_pur_serial_no_values(self, obj, serial_no, d, s, new_rec, rejected=None): - item_details = sql("""select item_group, warranty_period + item_details = webnotes.conn.sql("""select item_group, warranty_period from `tabItem` where name = '%s' and (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00' or end_of_life > now()) """ %(d.item_code), as_dict=1) @@ -112,7 +112,7 @@ class DocType: def update_serial_purchase_details(self, obj, d, serial_no, is_submit, purpose = '', rejected=None): - exists = sql("select name, status, docstatus from `tabSerial No` where name = '%s'" % (serial_no)) + exists = webnotes.conn.sql("select name, status, docstatus from `tabSerial No` where name = '%s'" % (serial_no)) if is_submit: if exists and exists[0][2] != 2 and purpose not in ['Material Transfer', 'Sales Return']: msgprint("Serial No: %s already %s" % (serial_no, exists and exists[0][1]), raise_exception = 1) @@ -126,15 +126,15 @@ class DocType: if exists and exists[0][1] == 'Delivered' and exists[0][2] != 2: msgprint("Serial No: %s is already delivered, you can not cancel the document." % serial_no, raise_exception=1) elif purpose == 'Material Transfer': - sql("update `tabSerial No` set status = 'In Store', purchase_document_type = '', purchase_document_no = '', warehouse = '%s' where name = '%s'" % (d.s_warehouse, serial_no)) + webnotes.conn.sql("update `tabSerial No` set status = 'In Store', purchase_document_type = '', purchase_document_no = '', warehouse = '%s' where name = '%s'" % (d.s_warehouse, serial_no)) elif purpose == 'Sales Return': - sql("update `tabSerial No` set status = 'Delivered', purchase_document_type = '', purchase_document_no = '' where name = '%s'" % serial_no) + webnotes.conn.sql("update `tabSerial No` set status = 'Delivered', purchase_document_type = '', purchase_document_no = '' where name = '%s'" % serial_no) else: - sql("update `tabSerial No` set docstatus = 2, status = 'Not in Use', purchase_document_type = '', purchase_document_no = '', purchase_date = null, purchase_rate = 0, supplier = null, supplier_name = '', supplier_address = '', warehouse = '' where name = '%s'" % serial_no) + webnotes.conn.sql("update `tabSerial No` set docstatus = 2, status = 'Not in Use', purchase_document_type = '', purchase_document_no = '', purchase_date = null, purchase_rate = 0, supplier = null, supplier_name = '', supplier_address = '', warehouse = '' where name = '%s'" % serial_no) def check_serial_no_exists(self, serial_no, item_code): - chk = sql("select name, status, docstatus, item_code from `tabSerial No` where name = %s", (serial_no), as_dict=1) + chk = webnotes.conn.sql("select name, status, docstatus, item_code from `tabSerial No` where name = %s", (serial_no), as_dict=1) if not chk: msgprint("Serial No: %s does not exists in the system" % serial_no, raise_exception=1) elif chk and chk[0]['item_code'] != item_code: @@ -169,7 +169,7 @@ class DocType: self.check_serial_no_exists(serial_no, d.item_code) self.set_delivery_serial_no_values(obj, serial_no) else: - sql("update `tabSerial No` set docstatus = 0, status = 'In Store', delivery_document_type = '', delivery_document_no = '', delivery_date = null, customer = null, customer_name = '', delivery_address = '', territory = null where name = '%s'" % (serial_no)) + webnotes.conn.sql("update `tabSerial No` set docstatus = 0, status = 'In Store', delivery_document_type = '', delivery_document_no = '', delivery_date = null, customer = null, customer_name = '', delivery_address = '', territory = null where name = '%s'" % (serial_no)) def update_serial_record(self, obj, fname, is_submit = 1, is_incoming = 0): @@ -202,8 +202,10 @@ class DocType: if v.get('is_cancelled') == 'Yes': v['actual_qty'] = -flt(v['actual_qty']) # cancel matching entry - sql("update `tabStock Ledger Entry` set is_cancelled='Yes' where voucher_no=%s \ - and voucher_type=%s", (v['voucher_no'], v['voucher_type'])) + webnotes.conn.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes', + modified=%s, modified_by=%s + where voucher_no=%s and voucher_type=%s""", + (now(), webnotes.session.user, v['voucher_no'], v['voucher_type'])) if v.get("actual_qty"): sle_id = self.make_entry(v) @@ -230,5 +232,5 @@ class DocType: """ Repost everything! """ - for wh in sql("select name from tabWarehouse"): + for wh in webnotes.conn.sql("select name from tabWarehouse"): get_obj('Warehouse', wh[0]).repost_stock() From 4f129239030db439a7eb04b23d6339ad9c25701d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 7 Mar 2013 18:49:50 +0530 Subject: [PATCH 4/4] fixes in sales team percentage validation --- selling/doctype/sales_common/sales_common.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py index 47d139f955..12ae4a8c90 100644 --- a/selling/doctype/sales_common/sales_common.py +++ b/selling/doctype/sales_common/sales_common.py @@ -341,12 +341,10 @@ class DocType(TransactionBase): # ======================================================================== # it indicates % contribution of sales person in sales def get_allocated_sum(self,obj): - sum = 0 - for d in getlist(obj.doclist,'sales_team'): - sum += flt(d.allocated_percentage) - if (flt(sum) != 100) and getlist(obj.doclist,'sales_team'): - msgprint("Total Allocated % of Sales Persons should be 100%") - raise Exception + sales_team_list = obj.doclist.get({"parentfield": "sales_team"}) + total_allocation = sum([flt(d.allocated_percentage) for d in sales_team_list]) + if sales_team_list and total_allocation != 100.0: + msgprint("Total Allocated %% of Sales Persons should be 100%", raise_exception=True) # Check Conversion Rate (i.e. it will not allow conversion rate to be 1 for Currency other than default currency set in Global Defaults) # ===========================================================================