From aeef7bd910c73c9261c2814d930bde677be51ab1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 19 Apr 2013 16:21:55 +0530 Subject: [PATCH 01/17] [sales taxes and charges] renamed field cost_center_other_charges to cost_center --- accounts/doctype/sales_invoice/sales_invoice.py | 2 +- .../sales_taxes_and_charges.txt | 4 ++-- .../sales_taxes_and_charges_master.js | 2 +- patches/april_2013/p06_default_cost_center.py | 12 ++++++------ .../p07_rename_cost_center_other_charges.py | 9 +++++++++ patches/patch_list.py | 1 + startup/boot.py | 5 ++--- 7 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 patches/april_2013/p07_rename_cost_center_other_charges.py diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index f44a787de9..cbacf58c5c 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -725,7 +725,7 @@ class DocType(SellingController): "against": self.doc.debit_to, "credit": flt(tax.tax_amount), "remarks": self.doc.remarks, - "cost_center": tax.cost_center_other_charges + "cost_center": tax.cost_center }) ) diff --git a/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt b/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt index 161eb008ea..55a41a9345 100644 --- a/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt +++ b/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt @@ -2,7 +2,7 @@ { "creation": "2013-02-22 01:27:41", "docstatus": 0, - "modified": "2013-04-17 14:05:18", + "modified": "2013-04-17 14:05:50", "modified_by": "Administrator", "owner": "Administrator" }, @@ -50,7 +50,7 @@ { "default": ":Company", "doctype": "DocField", - "fieldname": "cost_center_other_charges", + "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", "oldfieldname": "cost_center_other_charges", diff --git a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js index 1e72010f26..5787427339 100644 --- a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js +++ b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js @@ -140,7 +140,7 @@ cur_frm.fields_dict['other_charges'].grid.get_field("account_head").get_query = return 'SELECT tabAccount.name FROM tabAccount WHERE tabAccount.group_or_ledger="Ledger" AND tabAccount.docstatus != 2 AND tabAccount.account_type in ("Tax", "Chargeable", "Income Account") AND tabAccount.company = "'+doc.company+'" AND tabAccount.name LIKE "%s"' } -cur_frm.fields_dict['other_charges'].grid.get_field("cost_center_other_charges").get_query = function(doc) { +cur_frm.fields_dict['other_charges'].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'; } diff --git a/patches/april_2013/p06_default_cost_center.py b/patches/april_2013/p06_default_cost_center.py index 4f80d95d0e..4aaa7d53b3 100644 --- a/patches/april_2013/p06_default_cost_center.py +++ b/patches/april_2013/p06_default_cost_center.py @@ -1,10 +1,10 @@ import webnotes def execute(): - for dt, fieldname in \ - (("Journal Voucher Detail", "cost_center"), - ("Sales Taxes and Charges", "cost_center_other_charges"), - ("Purchase Taxes and Charges", "cost_center"), ("Delivery Note Item", "cost_center"), - ("Purchase Invoice Item", "cost_center"), ("Sales Invoice Item", "cost_center")): - webnotes.conn.sql_ddl("""alter table `tab%s` alter `%s` drop default""" % (dt, fieldname)) + webnotes.reload_doc("Stock", "DocType", "Delivery Note Item") + for dt in ("Journal Voucher Detail", "Sales Taxes and Charges", + "Purchase Taxes and Charges", "Delivery Note Item", + "Purchase Invoice Item", "Sales Invoice Item"): + webnotes.conn.sql_ddl("""alter table `tab%s` alter `cost_center` drop default""" \ + % (dt,)) webnotes.reload_doc(webnotes.conn.get_value("DocType", dt, "module"), "DocType", dt) diff --git a/patches/april_2013/p07_rename_cost_center_other_charges.py b/patches/april_2013/p07_rename_cost_center_other_charges.py new file mode 100644 index 0000000000..c3c94912cf --- /dev/null +++ b/patches/april_2013/p07_rename_cost_center_other_charges.py @@ -0,0 +1,9 @@ +import webnotes + +def execute(): + webnotes.reload_doc("Accounts", "DocType", "Sales Taxes and Charges") + webnotes.conn.sql("""update `tabSales Taxes and Charges` + set cost_center = cost_center_other_charges""") + webnotes.conn.sql_ddl("""alter table `tabSales Taxes and Charges` + drop column cost_center_other_charges""") + \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 8ad821ddc4..2cde56da35 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -244,5 +244,6 @@ patch_list = [ "patches.april_2013.p06_update_file_size", "patches.april_2013.p05_fixes_in_reverse_modules", "execute:webnotes.delete_doc('DocType Mapper', 'Delivery Note-Packing Slip')", + "patches.april_2013.p07_rename_cost_center_other_charges", "patches.april_2013.p06_default_cost_center", ] \ No newline at end of file diff --git a/startup/boot.py b/startup/boot.py index 9ed20ff73a..b202d17697 100644 --- a/startup/boot.py +++ b/startup/boot.py @@ -36,9 +36,8 @@ def boot_session(bootinfo): for key in ['max_users', 'expires_on', 'max_space', 'status', 'developer_mode']: if hasattr(conf, key): bootinfo[key] = getattr(conf, key) - bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center, - cost_center as 'cost_center_other_charges' from `tabCompany`""", - as_dict=1, update={"doctype":":Company"}) + bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center + from `tabCompany`""", as_dict=1, update={"doctype":":Company"}) def get_letter_heads(): """load letter heads with startup""" From 9b826a7cb23c9be2c63d266461ecd6ebcdb5440b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 19 Apr 2013 16:30:32 +0530 Subject: [PATCH 02/17] [patches] removed export file from update file data patch --- patches/april_2013/p05_update_file_data.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index a5540cc3cd..8811858382 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -1,5 +1,4 @@ import webnotes, webnotes.utils, os -from webnotes.modules.export_file import export_to_files def execute(): webnotes.reload_doc("core", "doctype", "file_data") @@ -26,7 +25,6 @@ def execute(): webnotes.conn.sql("""delete from tabDocField where fieldname='file_list' and parent=%s""", doctype) - export_to_files([["DocType", doctype]]) def update_for_doc(doctype, doc): for filedata in doc.file_list.split("\n"): From b8b1e58c4deaa50203fa01165a196b9891ff51be Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 6 May 2013 12:52:46 +0530 Subject: [PATCH 03/17] [buying controller] [fix] use discount_rate instead of discount --- controllers/buying_controller.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 9e181bc8fe..480214018c 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -140,31 +140,28 @@ class BuyingController(StockController): item.fields[base_field] = flt((flt(item.fields[print_field], self.precision.item[print_field]) * self.doc.conversion_rate), self.precision.item[base_field]) - - for item in self.item_doclist: - round_floats_in_doc(item, self.precision.item) + + # hack! - cleaned up in _cleanup() + if self.doc.doctype != "Purchase Invoice": + self.precision.item["rate"] = self.precision.item.purchase_rate + for item in self.item_doclist: # hack! - cleaned up in _cleanup() if self.doc.doctype != "Purchase Invoice": item.rate = item.purchase_rate - self.precision.item.rate = self.precision.item.purchase_rate - item.discount = item.discount_rate - self.precision.item.discount = self.precision.item.discount_rate + round_floats_in_doc(item, self.precision.item) - if item.discount == 100: - if not item.import_ref_rate: - item.import_ref_rate = item.import_rate + if item.discount_rate == 100: + item.import_ref_rate = item.import_ref_rate or item.import_rate item.import_rate = 0 else: if item.import_ref_rate: - item.import_rate = flt(item.import_ref_rate * - (1.0 - (item.discount_rate / 100.0)), + item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)), self.precision.item.import_rate) else: - # assume that print rate and discount are specified - item.import_ref_rate = flt(item.import_rate / - (1.0 - (item.discount_rate / 100.0)), + # assume that print rate and discount_rate are specified + item.import_ref_rate = flt(item.import_rate / (1.0 - (item.discount_rate / 100.0)), self.precision.item.import_ref_rate) item.import_amount = flt(item.import_rate * item.qty, @@ -297,9 +294,6 @@ class BuyingController(StockController): item.purchase_rate = item.rate del item.fields["rate"] - item.discount_rate = item.discount - del item.fields["discount"] - def validate_on_previous_row(self, tax): """ validate if a valid row id is mentioned in case of From 21f4ea36b21c40c97e5b10789514b0969bce68e5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 10 May 2013 18:08:32 +0530 Subject: [PATCH 04/17] [selling] [calculations] server side calculations, test cases and rounding based on currency number format --- .../doctype/sales_invoice/sales_invoice.txt | 22 +- .../sales_invoice/test_sales_invoice.py | 477 +++++++++++++++++- controllers/accounts_controller.py | 2 +- controllers/buying_controller.py | 64 +-- controllers/selling_controller.py | 281 ++++++++++- patches/may_2013/__init__.py | 0 .../may_2013/p01_selling_net_total_export.py | 10 + patches/patch_list.py | 1 + selling/doctype/quotation/quotation.txt | 14 +- selling/doctype/sales_common/sales_common.js | 2 + selling/doctype/sales_order/sales_order.txt | 19 +- stock/doctype/delivery_note/delivery_note.txt | 19 +- stock/doctype/stock_entry/test_stock_entry.py | 1 + 13 files changed, 843 insertions(+), 69 deletions(-) create mode 100644 patches/may_2013/__init__.py create mode 100644 patches/may_2013/p01_selling_net_total_export.py diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt index 9d8f54e73f..f1c0cab74d 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.txt +++ b/accounts/doctype/sales_invoice/sales_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-19 11:00:14", + "creation": "2013-05-06 12:03:41", "docstatus": 0, - "modified": "2013-04-22 11:59:40", + "modified": "2013-05-09 17:34:14", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,6 +10,7 @@ "allow_attach": 1, "autoname": "naming_series:", "doctype": "DocType", + "document_type": "Transaction", "is_submittable": 1, "module": "Accounts", "name": "__common__", @@ -30,6 +31,7 @@ "parent": "Sales Invoice", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1 }, { @@ -251,7 +253,6 @@ "width": "50%" }, { - "description": "Will be calculated automatically when you enter the details", "doctype": "DocField", "fieldname": "net_total", "fieldtype": "Currency", @@ -259,10 +260,19 @@ "oldfieldname": "net_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 0, + "print_hide": 1, "read_only": 1, "reqd": 1 }, + { + "doctype": "DocField", + "fieldname": "net_total_export", + "fieldtype": "Currency", + "label": "Net Total (Export)", + "options": "currency", + "print_hide": 0, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "recalculate_values", @@ -1288,7 +1298,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "permlevel": 0, "report": 1, "role": "Accounts User", "submit": 1, @@ -1297,8 +1306,7 @@ { "doctype": "DocPerm", "match": "customer", - "permlevel": 0, - "report": 1, + "report": 0, "role": "Customer" } ] \ No newline at end of file diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index b46cdd1777..505848a3c8 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1,12 +1,226 @@ import webnotes -import unittest +import unittest, json +from webnotes.utils import flt class TestSalesInvoice(unittest.TestCase): def make(self): - w = webnotes.bean(webnotes.copy_doclist(test_records[0])) + w = webnotes.bean(copy=test_records[0]) w.insert() w.submit() return w + + def test_sales_invoice_calculation_base_currency(self): + si = webnotes.bean(copy=test_records[2]) + si.run_method("calculate_taxes_and_totals") + si.insert() + + expected_values = { + "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount", + "base_ref_rate", "basic_rate", "amount"], + "_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500], + "_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750], + } + + # check if children are saved + self.assertEquals(len(si.doclist.get({"parentfield": "entries"})), + len(expected_values)-1) + + # check if item values are calculated + for d in si.doclist.get({"parentfield": "entries"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.doc.net_total, 1250) + self.assertEquals(si.doc.net_total_export, 1250) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "total"], + "_Test Account Shipping Charges - _TC": [100, 1350], + "_Test Account Customs Duty - _TC": [125, 1475], + "_Test Account Excise Duty - _TC": [140, 1615], + "_Test Account Education Cess - _TC": [2.8, 1617.8], + "_Test Account S&H Education Cess - _TC": [1.4, 1619.2], + "_Test Account CST - _TC": [32.38, 1651.58], + "_Test Account VAT - _TC": [156.25, 1807.83], + "_Test Account Discount - _TC": [-180.78, 1627.05] + } + + for d in si.doclist.get({"parentfield": "other_charges"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.account_head][i]) + + self.assertEquals(si.doc.grand_total, 1627.05) + self.assertEquals(si.doc.grand_total_export, 1627.05) + + def test_sales_invoice_calculation_export_currency(self): + si = webnotes.bean(copy=test_records[2]) + si.doc.currency = "USD" + si.doc.conversion_rate = 50 + si.doclist[1].export_rate = 1 + si.doclist[1].ref_rate = 1 + si.doclist[2].export_rate = 3 + si.doclist[2].ref_rate = 3 + si.run_method("calculate_taxes_and_totals") + si.insert() + + expected_values = { + "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount", + "base_ref_rate", "basic_rate", "amount"], + "_Test Item Home Desktop 100": [1, 0, 1, 10, 50, 50, 500], + "_Test Item Home Desktop 200": [3, 0, 3, 15, 150, 150, 750], + } + + # check if children are saved + self.assertEquals(len(si.doclist.get({"parentfield": "entries"})), + len(expected_values)-1) + + # check if item values are calculated + for d in si.doclist.get({"parentfield": "entries"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.doc.net_total, 1250) + self.assertEquals(si.doc.net_total_export, 25) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "total"], + "_Test Account Shipping Charges - _TC": [100, 1350], + "_Test Account Customs Duty - _TC": [125, 1475], + "_Test Account Excise Duty - _TC": [140, 1615], + "_Test Account Education Cess - _TC": [2.8, 1617.8], + "_Test Account S&H Education Cess - _TC": [1.4, 1619.2], + "_Test Account CST - _TC": [32.38, 1651.58], + "_Test Account VAT - _TC": [156.25, 1807.83], + "_Test Account Discount - _TC": [-180.78, 1627.05] + } + + for d in si.doclist.get({"parentfield": "other_charges"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.account_head][i]) + + self.assertEquals(si.doc.grand_total, 1627.05) + self.assertEquals(si.doc.grand_total_export, 32.54) + + def test_inclusive_rate_validations(self): + si = webnotes.bean(copy=test_records[2]) + for i, tax in enumerate(si.doclist.get({"parentfield": "other_charges"})): + tax.idx = i+1 + + si.doclist[1].export_rate = 62.5 + si.doclist[1].export_rate = 191 + for i in [3, 5, 6, 7, 8, 9]: + si.doclist[i].included_in_print_rate = 1 + + # tax type "Actual" cannot be inclusive + self.assertRaises(webnotes.ValidationError, si.run_method, "calculate_taxes_and_totals") + + # taxes above included type 'On Previous Row Total' should also be included + si.doclist[3].included_in_print_rate = 0 + self.assertRaises(webnotes.ValidationError, si.run_method, "calculate_taxes_and_totals") + + def test_sales_invoice_calculation_base_currency_with_tax_inclusive_price(self): + # prepare + si = webnotes.bean(copy=test_records[3]) + si.run_method("calculate_taxes_and_totals") + si.insert() + + expected_values = { + "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount", + "base_ref_rate", "basic_rate", "amount"], + "_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 50, 50, 500], + "_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 150, 150, 750], + } + + # check if children are saved + self.assertEquals(len(si.doclist.get({"parentfield": "entries"})), + len(expected_values)-1) + + # check if item values are calculated + for d in si.doclist.get({"parentfield": "entries"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.doc.net_total, 1250) + self.assertEquals(si.doc.net_total_export, 1578.3) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "total"], + "_Test Account Excise Duty - _TC": [140, 1390], + "_Test Account Education Cess - _TC": [2.8, 1392.8], + "_Test Account S&H Education Cess - _TC": [1.4, 1394.2], + "_Test Account CST - _TC": [27.88, 1422.08], + "_Test Account VAT - _TC": [156.25, 1578.33], + "_Test Account Customs Duty - _TC": [125, 1703.33], + "_Test Account Shipping Charges - _TC": [100, 1803.33], + "_Test Account Discount - _TC": [-180.33, 1623] + } + + for d in si.doclist.get({"parentfield": "other_charges"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(flt(d.fields.get(k), 6), expected_values[d.account_head][i]) + + self.assertEquals(si.doc.grand_total, 1623) + self.assertEquals(si.doc.grand_total_export, 1623) + + def test_sales_invoice_calculation_export_currency_with_tax_inclusive_price(self): + # prepare + si = webnotes.bean(copy=test_records[3]) + si.doc.currency = "USD" + si.doc.conversion_rate = 50 + si.doclist[1].export_rate = 50 + si.doclist[1].adj_rate = 10 + si.doclist[2].export_rate = 150 + si.doclist[2].adj_rate = 20 + si.doclist[9].rate = 5000 + + si.run_method("calculate_taxes_and_totals") + si.insert() + + expected_values = { + "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount", + "base_ref_rate", "basic_rate", "amount"], + "_Test Item Home Desktop 100": [55.56, 10, 50, 500, 2222.11, 1999.9, 19999.0], + "_Test Item Home Desktop 200": [187.5, 20, 150, 750, 7375.66, 5900.53, 29502.65], + } + + # check if children are saved + self.assertEquals(len(si.doclist.get({"parentfield": "entries"})), + len(expected_values)-1) + + # check if item values are calculated + for d in si.doclist.get({"parentfield": "entries"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.doc.net_total, 49501.65) + self.assertEquals(si.doc.net_total_export, 1250) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "total"], + "_Test Account Excise Duty - _TC": [5540.22, 55041.87], + "_Test Account Education Cess - _TC": [110.81, 55152.68], + "_Test Account S&H Education Cess - _TC": [55.4, 55208.08], + "_Test Account CST - _TC": [1104.16, 56312.24], + "_Test Account VAT - _TC": [6187.71, 62499.95], + "_Test Account Customs Duty - _TC": [4950.17, 67450.12], + "_Test Account Shipping Charges - _TC": [5000, 72450.12], + "_Test Account Discount - _TC": [-7245.01, 65205.11] + } + + for d in si.doclist.get({"parentfield": "other_charges"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(flt(d.fields.get(k), 6), expected_values[d.account_head][i]) + + self.assertEquals(si.doc.grand_total, 65205.11) + self.assertEquals(si.doc.grand_total_export, 1304.1) def test_outstanding(self): w = self.make() @@ -520,4 +734,263 @@ test_records = [ "tax_amount": 50.0, } ], + [ + { + "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_export": 0, + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", + "price_list_name": "_Test Price List", + "territory": "_Test Territory", + }, + # items + { + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "item_code": "_Test Item Home Desktop 100", + "item_name": "_Test Item Home Desktop 100", + "qty": 10, + "export_rate": 50, + "stock_uom": "_Test UOM", + "item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}), + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + + }, + { + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "item_code": "_Test Item Home Desktop 200", + "item_name": "_Test Item Home Desktop 200", + "qty": 5, + "export_rate": 150, + "stock_uom": "_Test UOM", + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + + }, + # taxes + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "Actual", + "account_head": "_Test Account Shipping Charges - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Shipping Charges", + "rate": 100 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account Customs Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Customs Duty", + "rate": 10 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account Excise Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "rate": 12 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Amount", + "account_head": "_Test Account Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "rate": 2, + "row_id": 3 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Amount", + "account_head": "_Test Account S&H Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "rate": 1, + "row_id": 3 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Total", + "account_head": "_Test Account CST - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "CST", + "rate": 2, + "row_id": 5 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account VAT - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "rate": 12.5 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Total", + "account_head": "_Test Account Discount - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Discount", + "rate": -10, + "row_id": 7 + }, + ], + [ + { + "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_export": 0, + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", + "price_list_name": "_Test Price List", + "territory": "_Test Territory", + }, + # items + { + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "item_code": "_Test Item Home Desktop 100", + "item_name": "_Test Item Home Desktop 100", + "qty": 10, + "export_rate": 62.5, + "stock_uom": "_Test UOM", + "item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}), + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + + }, + { + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "item_code": "_Test Item Home Desktop 200", + "item_name": "_Test Item Home Desktop 200", + "qty": 5, + "export_rate": 190.66, + "stock_uom": "_Test UOM", + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + + }, + # taxes + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account Excise Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "rate": 12, + "included_in_print_rate": 1, + "idx": 1 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Amount", + "account_head": "_Test Account Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "rate": 2, + "row_id": 1, + "included_in_print_rate": 1, + "idx": 2 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Amount", + "account_head": "_Test Account S&H Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "rate": 1, + "row_id": 1, + "included_in_print_rate": 1, + "idx": 3 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Total", + "account_head": "_Test Account CST - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "CST", + "rate": 2, + "row_id": 3, + "included_in_print_rate": 1, + "idx": 4 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account VAT - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "rate": 12.5, + "included_in_print_rate": 1, + "idx": 5 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account Customs Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Customs Duty", + "rate": 10, + "idx": 6 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "Actual", + "account_head": "_Test Account Shipping Charges - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Shipping Charges", + "rate": 100, + "idx": 7 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Total", + "account_head": "_Test Account Discount - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Discount", + "rate": -10, + "row_id": 7, + "idx": 8 + }, + ], ] \ No newline at end of file diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index 04e4bbdaee..19b2a50ff9 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -97,4 +97,4 @@ class AccountsController(TransactionBase): if not hasattr(self, "_abbr"): self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr") - return self._abbr \ No newline at end of file + return self._abbr diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 480214018c..d4aa2252eb 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -22,7 +22,6 @@ import json from buying.utils import get_item_details from setup.utils import get_company_currency -from webnotes.model.utils import round_floats_in_doc from controllers.stock_controller import StockController @@ -138,19 +137,20 @@ class BuyingController(StockController): def _set_base(item, print_field, base_field): """set values in base currency""" item.fields[base_field] = flt((flt(item.fields[print_field], - self.precision.item[print_field]) * self.doc.conversion_rate), - self.precision.item[base_field]) + self.precision_of(print_field, item.parentfield)) * self.doc.conversion_rate), + self.precision_of(base_field, item.parentfield)) # hack! - cleaned up in _cleanup() if self.doc.doctype != "Purchase Invoice": - self.precision.item["rate"] = self.precision.item.purchase_rate + df = self.meta.get_field("purchase_rate", parentfield=self.fname) + df.fieldname = "rate" for item in self.item_doclist: # hack! - cleaned up in _cleanup() if self.doc.doctype != "Purchase Invoice": item.rate = item.purchase_rate - round_floats_in_doc(item, self.precision.item) + self.round_floats_in_doc(item, item.parentfield) if item.discount_rate == 100: item.import_ref_rate = item.import_ref_rate or item.import_rate @@ -158,14 +158,14 @@ class BuyingController(StockController): else: if item.import_ref_rate: item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)), - self.precision.item.import_rate) + self.precision_of("import_rate", item.parentfield)) else: # assume that print rate and discount_rate are specified item.import_ref_rate = flt(item.import_rate / (1.0 - (item.discount_rate / 100.0)), - self.precision.item.import_ref_rate) + self.precision_of("import_ref_rate", item.parentfield)) item.import_amount = flt(item.import_rate * item.qty, - self.precision.item.import_amount) + self.precision_of("import_amount", item.parentfield)) _set_base(item, "import_ref_rate", "purchase_ref_rate") _set_base(item, "import_rate", "rate") @@ -183,7 +183,7 @@ class BuyingController(StockController): self.validate_on_previous_row(tax) - round_floats_in_doc(tax, self.precision.tax) + self.round_floats_in_doc(tax, tax.parentfield) def calculate_net_total(self): self.doc.net_total = 0 @@ -193,9 +193,9 @@ class BuyingController(StockController): self.doc.net_total += item.amount self.doc.net_total_import += item.import_amount - self.doc.net_total = flt(self.doc.net_total, self.precision.main.net_total) + self.doc.net_total = flt(self.doc.net_total, self.precision_of("net_total")) self.doc.net_total_import = flt(self.doc.net_total_import, - self.precision.main.net_total_import) + self.precision_of("net_total_import")) def calculate_taxes(self): for item in self.item_doclist: @@ -213,7 +213,7 @@ class BuyingController(StockController): # and tax.grand_total_for_current_item for the first such iteration if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ tax.charge_type=="Actual": - zero_net_total_adjustment = flt(tax.rate, self.precision.tax.tax_amount) + zero_net_total_adjustment = flt(tax.rate, self.precision_of("tax_amount", tax.parentfield)) current_tax_amount += zero_net_total_adjustment # store tax_amount for current item as it will be used for @@ -235,12 +235,12 @@ class BuyingController(StockController): # item's amount, previously applied tax and the current tax on that item if i==0: tax.grand_total_for_current_item = flt(item.amount + - current_tax_amount, self.precision.tax.total) + current_tax_amount, self.precision_of("total", tax.parentfield)) else: tax.grand_total_for_current_item = \ flt(self.tax_doclist[i-1].grand_total_for_current_item + - current_tax_amount, self.precision.tax.total) + current_tax_amount, self.precision_of("total", tax.parentfield)) # in tax.total, accumulate grand total of each item tax.total += tax.grand_total_for_current_item @@ -252,20 +252,20 @@ class BuyingController(StockController): def calculate_totals(self): if self.tax_doclist: self.doc.grand_total = flt(self.tax_doclist[-1].total, - self.precision.main.grand_total) + self.precision_of("grand_total")) self.doc.grand_total_import = flt( self.doc.grand_total / self.doc.conversion_rate, - self.precision.main.grand_total_import) + self.precision_of("grand_total_import")) else: self.doc.grand_total = flt(self.doc.net_total, - self.precision.main.grand_total) + self.precision_of("grand_total")) self.doc.grand_total_import = flt( self.doc.grand_total / self.doc.conversion_rate, - self.precision.main.grand_total_import) + self.precision_of("grand_total_import")) self.doc.total_tax = \ flt(self.doc.grand_total - self.doc.net_total, - self.precision.main.total_tax) + self.precision_of("total_tax")) if self.meta.get_field("rounded_total"): self.doc.rounded_total = round(self.doc.grand_total) @@ -276,11 +276,11 @@ class BuyingController(StockController): def calculate_outstanding_amount(self): if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus == 0: self.doc.total_advance = flt(self.doc.total_advance, - self.precision.main.total_advance) + self.precision_of("total_advance")) self.doc.total_amount_to_pay = flt(self.doc.grand_total - flt(self.doc.write_off_amount, - self.precision.main.write_off_amount), self.precision.main.total_amount_to_pay) + self.precision_of("write_off_amount")), self.precision_of("total_amount_to_pay")) self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance, - self.precision.main.outstanding_amount) + self.precision_of("outstanding_amount")) def _cleanup(self): for tax in self.tax_doclist: @@ -319,7 +319,7 @@ class BuyingController(StockController): if tax.charge_type == "Actual": # distribute the tax amount proportionally to each item row - actual = flt(tax.rate, self.precision.tax.tax_amount) + actual = flt(tax.rate, self.precision_of("tax_amount", tax.parentfield)) current_tax_amount = (self.doc.net_total and ((item.amount / self.doc.net_total) * actual) or 0) @@ -332,11 +332,11 @@ class BuyingController(StockController): current_tax_amount = (tax_rate / 100.0) * \ self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item - return flt(current_tax_amount, self.precision.tax.tax_amount) + return flt(current_tax_amount, self.precision_of("tax_amount", tax.parentfield)) def _get_tax_rate(self, tax, item_tax_map): if item_tax_map.has_key(tax.account_head): - return flt(item_tax_map.get(tax.account_head), self.precision.tax.rate) + return flt(item_tax_map.get(tax.account_head), self.precision_of("rate", tax.parentfield)) else: return tax.rate @@ -350,7 +350,7 @@ class BuyingController(StockController): if tax.category in ["Valuation", "Valuation and Total"] and \ item.item_code in self.stock_items: item.item_tax_amount += flt(current_tax_amount, - self.precision.item.item_tax_amount) + self.precision_of("item_tax_amount", item.parentfield)) # update valuation rate def update_valuation_rate(self, parentfield): @@ -427,18 +427,6 @@ class BuyingController(StockController): return bom_items - - @property - def precision(self): - if not hasattr(self, "_precision"): - self._precision = webnotes._dict() - self._precision.main = self.meta.get_precision_map() - self._precision.item = self.meta.get_precision_map(parentfield = self.fname) - if self.meta.get_field("purchase_tax_details"): - self._precision.tax = self.meta.get_precision_map(parentfield = \ - "purchase_tax_details") - return self._precision - @property def sub_contracted_items(self): if not hasattr(self, "_sub_contracted_items"): diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index b22042d0fe..b055ca4367 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -16,9 +16,10 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint +from webnotes.utils import cint, flt from setup.utils import get_company_currency from webnotes import msgprint, _ +import json from controllers.stock_controller import StockController @@ -70,4 +71,280 @@ class SellingController(StockController): if item.buying_amount and not item.cost_center: msgprint(_("""Cost Center is mandatory for item: """) + item.item_code, - raise_exception=1) \ No newline at end of file + raise_exception=1) + + def calculate_taxes_and_totals(self): + self.doc.conversion_rate = flt(self.doc.conversion_rate) + self.item_doclist = self.doclist.get({"parentfield": self.fname}) + self.tax_doclist = self.doclist.get({"parentfield": "other_charges"}) + + self.calculate_item_values() + self.initialize_taxes() + + self.determin_exclusive_rate() + + # TODO + # code: save net_total_export on client side + # print format: show net_total_export instead of net_total + + self.calculate_net_total() + self.calculate_taxes() + self.calculate_totals() + # self.calculate_outstanding_amount() + # + self._cleanup() + + def determin_exclusive_rate(self): + if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)): + # no inclusive tax + return + + for item in self.item_doclist: + item_tax_map = self._load_item_tax_rate(item.item_tax_rate) + cumulated_tax_fraction = 0 + for i, tax in enumerate(self.tax_doclist): + if cint(tax.included_in_print_rate): + tax.tax_fraction_for_current_item = \ + self.get_current_tax_fraction(tax, item_tax_map) + else: + tax.tax_fraction_for_current_item = 0 + + if i==0: + tax.grand_total_fraction_for_current_item = 1 + \ + tax.tax_fraction_for_current_item + else: + tax.grand_total_fraction_for_current_item = \ + self.tax_doclist[i-1].grand_total_fraction_for_current_item \ + + tax.tax_fraction_for_current_item + + cumulated_tax_fraction += tax.tax_fraction_for_current_item + + if cumulated_tax_fraction: + item.basic_rate = flt((item.export_rate * self.doc.conversion_rate) / + (1 + cumulated_tax_fraction), self.precision_of("basic_rate", item.parentfield)) + + item.amount = flt(item.basic_rate * item.qty, self.precision_of("amount", item.parentfield)) + + item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)), + self.precision_of("base_ref_rate", item.parentfield)) + + def get_current_tax_fraction(self, tax, item_tax_map): + """ + Get tax fraction for calculating tax exclusive amount + from tax inclusive amount + """ + current_tax_fraction = 0 + + if cint(tax.included_in_print_rate): + tax_rate = self._get_tax_rate(tax, item_tax_map) + + if tax.charge_type == "On Net Total": + current_tax_fraction = tax_rate / 100.0 + + elif tax.charge_type == "On Previous Row Amount": + current_tax_fraction = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item + + elif tax.charge_type == "On Previous Row Total": + current_tax_fraction = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item + + return current_tax_fraction + + def calculate_item_values(self): + def _set_base(item, print_field, base_field): + """set values in base currency""" + item.fields[base_field] = flt((flt(item.fields[print_field], + self.precision_of(print_field, item.parentfield)) * self.doc.conversion_rate), + self.precision_of(base_field, item.parentfield)) + + for item in self.item_doclist: + self.round_floats_in_doc(item, item.parentfield) + + if item.adj_rate == 100: + item.ref_rate = item.ref_rate or item.export_rate + item.export_rate = 0 + else: + if item.ref_rate: + item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)), + self.precision_of("export_rate", item.parentfield)) + else: + # assume that print rate and discount are specified + item.ref_rate = flt(item.export_rate / (1.0 - (item.adj_rate / 100.0)), + self.precision_of("ref_rate", item.parentfield)) + + item.export_amount = flt(item.export_rate * item.qty, + self.precision_of("export_amount", item.parentfield)) + + _set_base(item, "ref_rate", "base_ref_rate") + _set_base(item, "export_rate", "basic_rate") + _set_base(item, "export_amount", "amount") + + def initialize_taxes(self): + for tax in self.tax_doclist: + tax.tax_amount = tax.total = 0.0 + # temporary fields + tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0 + tax.item_wise_tax_detail = {} + self.validate_on_previous_row(tax) + self.validate_inclusive_tax(tax) + self.round_floats_in_doc(tax, tax.parentfield) + + def calculate_net_total(self): + self.doc.net_total = 0 + self.doc.net_total_export = 0 + + for item in self.item_doclist: + self.doc.net_total += item.amount + self.doc.net_total_export += item.export_amount + + self.doc.net_total = flt(self.doc.net_total, self.precision_of("net_total")) + self.doc.net_total_export = flt(self.doc.net_total_export, + self.precision_of("net_total_export")) + + def calculate_taxes(self): + for item in self.item_doclist: + item_tax_map = self._load_item_tax_rate(item.item_tax_rate) + + for i, tax in enumerate(self.tax_doclist): + # tax_amount represents the amount of tax for the current step + current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map) + + # case when net total is 0 but there is an actual type charge + # in this case add the actual amount to tax.tax_amount + # and tax.grand_total_for_current_item for the first such iteration + if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ + tax.charge_type=="Actual": + zero_net_total_adjustment = flt(tax.rate, self.precision_of("tax_amount", tax.parentfield)) + current_tax_amount += zero_net_total_adjustment + + # store tax_amount for current item as it will be used for + # charge type = 'On Previous Row Amount' + tax.tax_amount_for_current_item = current_tax_amount + + # accumulate tax amount into tax.tax_amount + tax.tax_amount += tax.tax_amount_for_current_item + + # Calculate tax.total viz. grand total till that step + # note: grand_total_for_current_item contains the contribution of + # item's amount, previously applied tax and the current tax on that item + if i==0: + tax.grand_total_for_current_item = flt(item.amount + + current_tax_amount, self.precision_of("total", tax.parentfield)) + + else: + tax.grand_total_for_current_item = \ + flt(self.tax_doclist[i-1].grand_total_for_current_item + + current_tax_amount, self.precision_of("total", tax.parentfield)) + + # in tax.total, accumulate grand total of each item + tax.total += tax.grand_total_for_current_item + + # store tax_breakup for each item + # DOUBT: should valuation type amount also be stored? + tax.item_wise_tax_detail[item.item_code] = current_tax_amount + + def calculate_totals(self): + self.doc.grand_total = flt(self.tax_doclist and \ + self.tax_doclist[-1].total or self.doc.net_total, self.precision_of("grand_total")) + self.doc.grand_total_export = flt(self.doc.grand_total / self.doc.conversion_rate, + self.precision_of("grand_total_export")) + + self.doc.rounded_total = round(self.doc.grand_total) + self.doc.rounded_total_export = round(self.doc.grand_total_export) + + def get_current_tax_amount(self, item, tax, item_tax_map): + tax_rate = self._get_tax_rate(tax, item_tax_map) + + if tax.charge_type == "Actual": + # distribute the tax amount proportionally to each item row + actual = flt(tax.rate, self.precision_of("tax_amount", tax.parentfield)) + current_tax_amount = (self.doc.net_total + and ((item.amount / self.doc.net_total) * actual) + or 0) + elif tax.charge_type == "On Net Total": + current_tax_amount = (tax_rate / 100.0) * item.amount + elif tax.charge_type == "On Previous Row Amount": + current_tax_amount = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item + elif tax.charge_type == "On Previous Row Total": + current_tax_amount = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item + + return flt(current_tax_amount, self.precision_of("tax_amount", tax.parentfield)) + + def validate_on_previous_row(self, tax): + """ + validate if a valid row id is mentioned in case of + On Previous Row Amount and On Previous Row Total + """ + if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \ + (not tax.row_id or cint(tax.row_id) >= tax.idx): + msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \ + _("Please specify a valid") + " %(row_id_label)s") % { + "idx": tax.idx, + "taxes_doctype": tax.parenttype, + "row_id_label": self.meta.get_label("row_id", + parentfield="other_charges") + }, raise_exception=True) + + def validate_inclusive_tax(self, tax): + def _on_previous_row_error(tax, row_range): + msgprint((_("Row") + + " # %(idx)s [%(taxes_doctype)s] [%(charge_type_label)s = \"%(charge_type)s\"]: " + + _("If:") + ' "%(inclusive_label)s" = ' + _("checked") + ", " + + _("then it is required that:") + " [" + _("Row") + " # %(row_range)s] " + + '"%(inclusive_label)s" = ' + _("checked")) % { + "idx": tax.idx, + "taxes_doctype": tax.doctype, + "inclusive_label": self.meta.get_label("included_in_print_rate", + parentfield="other_charges"), + "charge_type_label": self.meta.get_label("charge_type", + parentfield="other_charges"), + "charge_type": tax.charge_type, + "row_range": row_range + }, raise_exception=True) + + if cint(tax.included_in_print_rate): + if tax.charge_type == "Actual": + # inclusive cannot be of type Actual + msgprint((_("Row") + + " # %(idx)s [%(taxes_doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" " + + "cannot be included in Item's rate") % { + "idx": tax.idx, + "taxes_doctype": tax.doctype, + "charge_type_label": self.meta.get_label("charge_type", + parentfield="other_charges"), + "charge_type": tax.charge_type, + }, raise_exception=True) + elif tax.charge_type == "On Previous Row Amount" and \ + not cint(self.tax_doclist[tax.row_id - 1].included_in_print_rate): + # referred row should also be inclusive + _on_previous_row_error(tax, tax.row_id) + elif tax.charge_type == "On Previous Row Total" and \ + not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:tax.idx - 1]]): + # all rows about this tax should be inclusive + _on_previous_row_error(tax, "1 - %d" % (tax.idx - 1,)) + + def _load_item_tax_rate(self, item_tax_rate): + if not item_tax_rate: + return {} + return json.loads(item_tax_rate) + + def _get_tax_rate(self, tax, item_tax_map): + if item_tax_map.has_key(tax.account_head): + return flt(item_tax_map.get(tax.account_head), self.precision_of("rate", tax.parentfield)) + else: + return tax.rate + + def _cleanup(self): + for tax in self.tax_doclist: + del tax.fields["grand_total_for_current_item"] + del tax.fields["tax_amount_for_current_item"] + + for fieldname in ("tax_fraction_for_current_item", + "grand_total_fraction_for_current_item"): + if fieldname in tax.fields: + del tax.fields[fieldname] + + tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail) diff --git a/patches/may_2013/__init__.py b/patches/may_2013/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/patches/may_2013/p01_selling_net_total_export.py b/patches/may_2013/p01_selling_net_total_export.py new file mode 100644 index 0000000000..dd0f68ac0a --- /dev/null +++ b/patches/may_2013/p01_selling_net_total_export.py @@ -0,0 +1,10 @@ +from __future__ import unicode_literals +import webnotes + +def execute(): + for module, doctype in (("Accounts", "Sales Invoice"), ("Selling", "Sales Order"), ("Selling", "Quotation"), + ("Stock", "Delivery Note")): + webnotes.reload_doc(module, "DocType", doctype) + webnotes.conn.sql("""update `tab%s` + set net_total_export = round(net_total / if(conversion_rate=0, 1, ifnull(conversion_rate, 1)), 2)""" % + (doctype,)) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 432f8f9d35..0a1370e453 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -250,4 +250,5 @@ patch_list = [ "patches.april_2013.p07_update_file_data_2", "patches.april_2013.rebuild_sales_browser", "patches.april_2013.p08_price_list_country", + "patches.may_2013.p01_selling_net_total_export", ] \ No newline at end of file diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt index feda14c591..24a080bac1 100644 --- a/selling/doctype/quotation/quotation.txt +++ b/selling/doctype/quotation/quotation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-03 09:10:44", + "creation": "2013-05-06 12:03:40", "docstatus": 0, - "modified": "2013-04-03 09:58:02", + "modified": "2013-05-06 13:07:37", "modified_by": "Administrator", "owner": "Administrator" }, @@ -239,11 +239,19 @@ "oldfieldname": "net_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 0, + "print_hide": 1, "read_only": 1, "reqd": 0, "width": "100px" }, + { + "doctype": "DocField", + "fieldname": "net_total_export", + "fieldtype": "Currency", + "label": "Net Total (Export)", + "options": "currency", + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "recalculate_values", diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js index 8a8d8d0a6c..1d020e6780 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -518,6 +518,8 @@ cur_frm.cscript.calc_doc_values = function(doc, cdt, cdn, tname, fname, other_fn if(flt(doc.conversion_rate)>1) { net_total_incl *= flt(doc.conversion_rate); } + + // TODO: store net_total_export doc.net_total = inclusive_rate ? flt(net_total_incl) : flt(net_total); doc.other_charges_total = roundNumber(flt(other_charges_total), 2); diff --git a/selling/doctype/sales_order/sales_order.txt b/selling/doctype/sales_order/sales_order.txt index ba0b1de07c..9780dc71a3 100644 --- a/selling/doctype/sales_order/sales_order.txt +++ b/selling/doctype/sales_order/sales_order.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-07 14:48:34", + "creation": "2013-05-06 12:03:43", "docstatus": 0, - "modified": "2013-01-29 17:14:58", + "modified": "2013-05-06 13:06:37", "modified_by": "Administrator", "owner": "Administrator" }, @@ -251,11 +251,19 @@ "oldfieldname": "net_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 0, + "print_hide": 1, "read_only": 1, "reqd": 0, "width": "150px" }, + { + "doctype": "DocField", + "fieldname": "net_total_export", + "fieldtype": "Currency", + "label": "Net Total (Export)", + "options": "currency", + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "recalculate_values", @@ -955,7 +963,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Sales Manager", @@ -978,7 +985,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "permlevel": 0, "report": 1, "role": "Sales User", @@ -990,7 +996,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Sales User", @@ -1013,7 +1018,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "Maintenance Manager", "submit": 0 @@ -1034,7 +1038,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "Maintenance User", "submit": 0 diff --git a/stock/doctype/delivery_note/delivery_note.txt b/stock/doctype/delivery_note/delivery_note.txt index 36c2789bfa..6f299efb15 100644 --- a/stock/doctype/delivery_note/delivery_note.txt +++ b/stock/doctype/delivery_note/delivery_note.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-02 10:50:50", + "creation": "2013-05-06 12:03:30", "docstatus": 0, - "modified": "2013-02-02 19:18:38", + "modified": "2013-05-06 13:08:13", "modified_by": "Administrator", "owner": "Administrator" }, @@ -255,12 +255,20 @@ "oldfieldname": "net_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 0, + "print_hide": 1, "print_width": "150px", "read_only": 1, "reqd": 0, "width": "150px" }, + { + "doctype": "DocField", + "fieldname": "net_total_export", + "fieldtype": "Currency", + "label": "Net Total (Export)", + "options": "currency", + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "recalculate_values", @@ -1136,7 +1144,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material User", @@ -1159,7 +1166,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material Manager", @@ -1171,7 +1177,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "permlevel": 0, "report": 1, "role": "Sales User", @@ -1183,7 +1188,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Sales User", @@ -1205,7 +1209,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "Accounts User", "submit": 0 diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index c3ce2d7f40..a9281cd004 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -450,6 +450,7 @@ class TestStockEntry(unittest.TestCase): for d in pi.doclist.get({"parentfield": "entries"}): d.expense_head = "_Test Account Cost for Goods Sold - _TC" d.cost_center = "_Test Cost Center - _TC" + for d in pi.doclist.get({"parentfield": "purchase_tax_details"}): d.cost_center = "_Test Cost Center - _TC" From 5af812a832570d5df27074a6cd09c50b7c28419f Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 10 May 2013 19:23:02 +0530 Subject: [PATCH 05/17] [precision] [fixes] --- .../purchase_invoice/purchase_invoice.py | 6 +- controllers/buying_controller.py | 80 +++++++++++-------- controllers/selling_controller.py | 36 ++++----- 3 files changed, 66 insertions(+), 56 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index c53b6d94fc..f7220bf214 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -466,9 +466,9 @@ class DocType(BuyingController): # expense will be booked in sales invoice stock_item_and_auto_inventory_accounting = True - valuation_amt = (flt(item.amount, self.precision.item.amount) + - flt(item.item_tax_amount, self.precision.item.item_tax_amount) + - flt(item.rm_supp_cost, self.precision.item.rm_supp_cost)) + valuation_amt = (flt(item.amount, self.precision("amount", item.parentfield)) + + flt(item.item_tax_amount, self.precision("item_tax_amount", item.parentfield)) + + flt(item.rm_supp_cost, self.precision("rm_supp_cost", item.parentfield))) gl_entries.append( self.get_gl_dict({ diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 17b4c8fc56..bc219ff184 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -137,8 +137,8 @@ class BuyingController(StockController): def _set_base(item, print_field, base_field): """set values in base currency""" item.fields[base_field] = flt((flt(item.fields[print_field], - self.precision_of(print_field, item.parentfield)) * self.doc.conversion_rate), - self.precision_of(base_field, item.parentfield)) + self.precision(print_field, item.parentfield)) * self.doc.conversion_rate), + self.precision(base_field, item.parentfield)) # hack! - cleaned up in _cleanup() if self.doc.doctype != "Purchase Invoice": @@ -158,19 +158,19 @@ class BuyingController(StockController): else: if item.import_ref_rate: item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)), - self.precision_of("import_rate", item.parentfield)) + self.precision("import_rate", item.parentfield)) else: # assume that print rate and discount_rate are specified item.import_ref_rate = flt(item.import_rate / (1.0 - (item.discount_rate / 100.0)), - self.precision_of("import_ref_rate", item.parentfield)) + self.precision("import_ref_rate", item.parentfield)) item.import_amount = flt(item.import_rate * item.qty, - self.precision_of("import_amount", item.parentfield)) + self.precision("import_amount", item.parentfield)) _set_base(item, "import_ref_rate", "purchase_ref_rate") _set_base(item, "import_rate", "rate") _set_base(item, "import_amount", "amount") - + def initialize_taxes(self): for tax in self.tax_doclist: # initialize totals to 0 @@ -193,9 +193,9 @@ class BuyingController(StockController): self.doc.net_total += item.amount self.doc.net_total_import += item.import_amount - self.doc.net_total = flt(self.doc.net_total, self.precision_of("net_total")) + self.doc.net_total = flt(self.doc.net_total, self.precision("net_total")) self.doc.net_total_import = flt(self.doc.net_total_import, - self.precision_of("net_total_import")) + self.precision("net_total_import")) def calculate_taxes(self): for item in self.item_doclist: @@ -213,7 +213,7 @@ class BuyingController(StockController): # and tax.grand_total_for_current_item for the first such iteration if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ tax.charge_type=="Actual": - zero_net_total_adjustment = flt(tax.rate, self.precision_of("tax_amount", tax.parentfield)) + zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax.parentfield)) current_tax_amount += zero_net_total_adjustment # store tax_amount for current item as it will be used for @@ -235,12 +235,12 @@ class BuyingController(StockController): # item's amount, previously applied tax and the current tax on that item if i==0: tax.grand_total_for_current_item = flt(item.amount + - current_tax_amount, self.precision_of("total", tax.parentfield)) + current_tax_amount, self.precision("total", tax.parentfield)) else: tax.grand_total_for_current_item = \ flt(self.tax_doclist[i-1].grand_total_for_current_item + - current_tax_amount, self.precision_of("total", tax.parentfield)) + current_tax_amount, self.precision("total", tax.parentfield)) # in tax.total, accumulate grand total of each item tax.total += tax.grand_total_for_current_item @@ -252,20 +252,20 @@ class BuyingController(StockController): def calculate_totals(self): if self.tax_doclist: self.doc.grand_total = flt(self.tax_doclist[-1].total, - self.precision_of("grand_total")) + self.precision("grand_total")) self.doc.grand_total_import = flt( self.doc.grand_total / self.doc.conversion_rate, - self.precision_of("grand_total_import")) + self.precision("grand_total_import")) else: self.doc.grand_total = flt(self.doc.net_total, - self.precision_of("grand_total")) + self.precision("grand_total")) self.doc.grand_total_import = flt( self.doc.grand_total / self.doc.conversion_rate, - self.precision_of("grand_total_import")) + self.precision("grand_total_import")) self.doc.total_tax = \ flt(self.doc.grand_total - self.doc.net_total, - self.precision_of("total_tax")) + self.precision("total_tax")) if self.meta.get_field("rounded_total"): self.doc.rounded_total = round(self.doc.grand_total) @@ -276,11 +276,11 @@ class BuyingController(StockController): def calculate_outstanding_amount(self): if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus == 0: self.doc.total_advance = flt(self.doc.total_advance, - self.precision_of("total_advance")) + self.precision("total_advance")) self.doc.total_amount_to_pay = flt(self.doc.grand_total - flt(self.doc.write_off_amount, - self.precision_of("write_off_amount")), self.precision_of("total_amount_to_pay")) + self.precision("write_off_amount")), self.precision("total_amount_to_pay")) self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance, - self.precision_of("outstanding_amount")) + self.precision("outstanding_amount")) def _cleanup(self): for tax in self.tax_doclist: @@ -294,6 +294,11 @@ class BuyingController(StockController): item.purchase_rate = item.rate del item.fields["rate"] + # reset fieldname of rate + if self.doc.doctype != "Purchase Invoice": + df = self.meta.get_field("rate", parentfield=self.fname) + df.fieldname = "purchase_rate" + def validate_on_previous_row(self, tax): """ validate if a valid row id is mentioned in case of @@ -319,7 +324,7 @@ class BuyingController(StockController): if tax.charge_type == "Actual": # distribute the tax amount proportionally to each item row - actual = flt(tax.rate, self.precision_of("tax_amount", tax.parentfield)) + actual = flt(tax.rate, self.precision("tax_amount", tax.parentfield)) current_tax_amount = (self.doc.net_total and ((item.amount / self.doc.net_total) * actual) or 0) @@ -332,11 +337,11 @@ class BuyingController(StockController): current_tax_amount = (tax_rate / 100.0) * \ self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item - return flt(current_tax_amount, self.precision_of("tax_amount", tax.parentfield)) + return flt(current_tax_amount, self.precision("tax_amount", tax.parentfield)) def _get_tax_rate(self, tax, item_tax_map): if item_tax_map.has_key(tax.account_head): - return flt(item_tax_map.get(tax.account_head), self.precision_of("rate", tax.parentfield)) + return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax.parentfield)) else: return tax.rate @@ -350,26 +355,31 @@ class BuyingController(StockController): if tax.category in ["Valuation", "Valuation and Total"] and \ item.item_code in self.stock_items: item.item_tax_amount += flt(current_tax_amount, - self.precision_of("item_tax_amount", item.parentfield)) + self.precision("item_tax_amount", item.parentfield)) # update valuation rate def update_valuation_rate(self, parentfield): - for d in self.doclist.get({"parentfield": parentfield}): - d.conversion_factor = d.conversion_factor or flt(webnotes.conn.get_value( - "UOM Conversion Detail", {"parent": d.item_code, "uom": d.uom}, + for item in self.doclist.get({"parentfield": parentfield}): + item.conversion_factor = item.conversion_factor or flt(webnotes.conn.get_value( + "UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom}, "conversion_factor")) or 1 - if d.item_code and d.qty: + if item.item_code and item.qty: + if self.doc.doctype == "Purchase Invoice": + purchase_rate = flt(item.rate, self.precision("rate", item.parentfield)) + else: + purchase_rate = flt(item.purchase_rate, self.precision("purchase_rate", item.parentfield)) + # if no item code, which is sometimes the case in purchase invoice, # then it is not possible to track valuation against it - d.valuation_rate = flt((flt(d.purchase_rate, self.precision.item.purchase_rate) or - flt(d.rate, self.precision.item.rate) + - (flt(d.item_tax_amount, self.precision.item.item_tax_amount) + - flt(d.rm_supp_cost, self.precision.item.rm_supp_cost)) / - flt(d.qty, self.precision.item.qty)) / - flt(d.conversion_factor, self.precision.item.conversion_factor), - self.precision.item.valuation_rate) + item.valuation_rate = flt( + (purchase_rate + \ + (flt(item.item_tax_amount, self.precision("item_tax_amount", item.parentfield)) + + flt(item.rm_supp_cost, self.precision("rm_supp_cost", item.parentfield)) + ) / flt(item.qty, self.precision("qty", item.parentfield)) + ) / flt(item.conversion_factor, self.precision("conversion_factor", item.parentfield)), + self.precision("valuation_rate", item.parentfield)) else: - d.valuation_rate = 0.0 + item.valuation_rate = 0.0 def validate_for_subcontracting(self): if not self.doc.is_subcontracted and self.sub_contracted_items: diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 65d75310fe..020e2af315 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -122,12 +122,12 @@ class SellingController(StockController): if cumulated_tax_fraction: item.basic_rate = flt((item.export_rate * self.doc.conversion_rate) / - (1 + cumulated_tax_fraction), self.precision_of("basic_rate", item.parentfield)) + (1 + cumulated_tax_fraction), self.precision("basic_rate", item.parentfield)) - item.amount = flt(item.basic_rate * item.qty, self.precision_of("amount", item.parentfield)) + item.amount = flt(item.basic_rate * item.qty, self.precision("amount", item.parentfield)) item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)), - self.precision_of("base_ref_rate", item.parentfield)) + self.precision("base_ref_rate", item.parentfield)) def get_current_tax_fraction(self, tax, item_tax_map): """ @@ -156,8 +156,8 @@ class SellingController(StockController): def _set_base(item, print_field, base_field): """set values in base currency""" item.fields[base_field] = flt((flt(item.fields[print_field], - self.precision_of(print_field, item.parentfield)) * self.doc.conversion_rate), - self.precision_of(base_field, item.parentfield)) + self.precision(print_field, item.parentfield)) * self.doc.conversion_rate), + self.precision(base_field, item.parentfield)) for item in self.item_doclist: self.round_floats_in_doc(item, item.parentfield) @@ -168,14 +168,14 @@ class SellingController(StockController): else: if item.ref_rate: item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)), - self.precision_of("export_rate", item.parentfield)) + self.precision("export_rate", item.parentfield)) else: # assume that print rate and discount are specified item.ref_rate = flt(item.export_rate / (1.0 - (item.adj_rate / 100.0)), - self.precision_of("ref_rate", item.parentfield)) + self.precision("ref_rate", item.parentfield)) item.export_amount = flt(item.export_rate * item.qty, - self.precision_of("export_amount", item.parentfield)) + self.precision("export_amount", item.parentfield)) _set_base(item, "ref_rate", "base_ref_rate") _set_base(item, "export_rate", "basic_rate") @@ -199,9 +199,9 @@ class SellingController(StockController): self.doc.net_total += item.amount self.doc.net_total_export += item.export_amount - self.doc.net_total = flt(self.doc.net_total, self.precision_of("net_total")) + self.doc.net_total = flt(self.doc.net_total, self.precision("net_total")) self.doc.net_total_export = flt(self.doc.net_total_export, - self.precision_of("net_total_export")) + self.precision("net_total_export")) def calculate_taxes(self): for item in self.item_doclist: @@ -216,7 +216,7 @@ class SellingController(StockController): # and tax.grand_total_for_current_item for the first such iteration if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ tax.charge_type=="Actual": - zero_net_total_adjustment = flt(tax.rate, self.precision_of("tax_amount", tax.parentfield)) + zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax.parentfield)) current_tax_amount += zero_net_total_adjustment # store tax_amount for current item as it will be used for @@ -231,12 +231,12 @@ class SellingController(StockController): # item's amount, previously applied tax and the current tax on that item if i==0: tax.grand_total_for_current_item = flt(item.amount + - current_tax_amount, self.precision_of("total", tax.parentfield)) + current_tax_amount, self.precision("total", tax.parentfield)) else: tax.grand_total_for_current_item = \ flt(self.tax_doclist[i-1].grand_total_for_current_item + - current_tax_amount, self.precision_of("total", tax.parentfield)) + current_tax_amount, self.precision("total", tax.parentfield)) # in tax.total, accumulate grand total of each item tax.total += tax.grand_total_for_current_item @@ -247,9 +247,9 @@ class SellingController(StockController): def calculate_totals(self): self.doc.grand_total = flt(self.tax_doclist and \ - self.tax_doclist[-1].total or self.doc.net_total, self.precision_of("grand_total")) + self.tax_doclist[-1].total or self.doc.net_total, self.precision("grand_total")) self.doc.grand_total_export = flt(self.doc.grand_total / self.doc.conversion_rate, - self.precision_of("grand_total_export")) + self.precision("grand_total_export")) self.doc.rounded_total = round(self.doc.grand_total) self.doc.rounded_total_export = round(self.doc.grand_total_export) @@ -259,7 +259,7 @@ class SellingController(StockController): if tax.charge_type == "Actual": # distribute the tax amount proportionally to each item row - actual = flt(tax.rate, self.precision_of("tax_amount", tax.parentfield)) + actual = flt(tax.rate, self.precision("tax_amount", tax.parentfield)) current_tax_amount = (self.doc.net_total and ((item.amount / self.doc.net_total) * actual) or 0) @@ -272,7 +272,7 @@ class SellingController(StockController): current_tax_amount = (tax_rate / 100.0) * \ self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item - return flt(current_tax_amount, self.precision_of("tax_amount", tax.parentfield)) + return flt(current_tax_amount, self.precision("tax_amount", tax.parentfield)) def validate_on_previous_row(self, tax): """ @@ -334,7 +334,7 @@ class SellingController(StockController): def _get_tax_rate(self, tax, item_tax_map): if item_tax_map.has_key(tax.account_head): - return flt(item_tax_map.get(tax.account_head), self.precision_of("rate", tax.parentfield)) + return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax.parentfield)) else: return tax.rate From 39384d36ea619687e6f984d5beba10b68570d1b9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Sat, 11 May 2013 19:39:53 +0530 Subject: [PATCH 06/17] [precision] server side cleanup and client side precision method --- .../purchase_invoice/purchase_invoice.py | 6 +-- controllers/buying_controller.py | 48 +++++++++---------- controllers/selling_controller.py | 38 ++++++++------- public/js/utils.js | 5 +- 4 files changed, 48 insertions(+), 49 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index f7220bf214..b63a176eba 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -466,9 +466,9 @@ class DocType(BuyingController): # expense will be booked in sales invoice stock_item_and_auto_inventory_accounting = True - valuation_amt = (flt(item.amount, self.precision("amount", item.parentfield)) + - flt(item.item_tax_amount, self.precision("item_tax_amount", item.parentfield)) + - flt(item.rm_supp_cost, self.precision("rm_supp_cost", item.parentfield))) + valuation_amt = (flt(item.amount, self.precision("amount", item)) + + flt(item.item_tax_amount, self.precision("item_tax_amount", item)) + + flt(item.rm_supp_cost, self.precision("rm_supp_cost", item))) gl_entries.append( self.get_gl_dict({ diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index bc219ff184..30c3dbbefa 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -137,8 +137,8 @@ class BuyingController(StockController): def _set_base(item, print_field, base_field): """set values in base currency""" item.fields[base_field] = flt((flt(item.fields[print_field], - self.precision(print_field, item.parentfield)) * self.doc.conversion_rate), - self.precision(base_field, item.parentfield)) + self.precision(print_field, item)) * self.doc.conversion_rate), + self.precision(base_field, item)) # hack! - cleaned up in _cleanup() if self.doc.doctype != "Purchase Invoice": @@ -150,7 +150,7 @@ class BuyingController(StockController): if self.doc.doctype != "Purchase Invoice": item.rate = item.purchase_rate - self.round_floats_in_doc(item, item.parentfield) + self.round_floats_in(item) if item.discount_rate == 100: item.import_ref_rate = item.import_ref_rate or item.import_rate @@ -158,14 +158,14 @@ class BuyingController(StockController): else: if item.import_ref_rate: item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)), - self.precision("import_rate", item.parentfield)) + self.precision("import_rate", item)) else: # assume that print rate and discount_rate are specified item.import_ref_rate = flt(item.import_rate / (1.0 - (item.discount_rate / 100.0)), - self.precision("import_ref_rate", item.parentfield)) + self.precision("import_ref_rate", item)) item.import_amount = flt(item.import_rate * item.qty, - self.precision("import_amount", item.parentfield)) + self.precision("import_amount", item)) _set_base(item, "import_ref_rate", "purchase_ref_rate") _set_base(item, "import_rate", "rate") @@ -183,7 +183,7 @@ class BuyingController(StockController): self.validate_on_previous_row(tax) - self.round_floats_in_doc(tax, tax.parentfield) + self.round_floats_in(tax) def calculate_net_total(self): self.doc.net_total = 0 @@ -213,7 +213,7 @@ class BuyingController(StockController): # and tax.grand_total_for_current_item for the first such iteration if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ tax.charge_type=="Actual": - zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax.parentfield)) + zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax)) current_tax_amount += zero_net_total_adjustment # store tax_amount for current item as it will be used for @@ -235,12 +235,12 @@ class BuyingController(StockController): # item's amount, previously applied tax and the current tax on that item if i==0: tax.grand_total_for_current_item = flt(item.amount + - current_tax_amount, self.precision("total", tax.parentfield)) + current_tax_amount, self.precision("total", tax)) else: tax.grand_total_for_current_item = \ flt(self.tax_doclist[i-1].grand_total_for_current_item + - current_tax_amount, self.precision("total", tax.parentfield)) + current_tax_amount, self.precision("total", tax)) # in tax.total, accumulate grand total of each item tax.total += tax.grand_total_for_current_item @@ -324,7 +324,7 @@ class BuyingController(StockController): if tax.charge_type == "Actual": # distribute the tax amount proportionally to each item row - actual = flt(tax.rate, self.precision("tax_amount", tax.parentfield)) + actual = flt(tax.rate, self.precision("tax_amount", tax)) current_tax_amount = (self.doc.net_total and ((item.amount / self.doc.net_total) * actual) or 0) @@ -337,11 +337,11 @@ class BuyingController(StockController): current_tax_amount = (tax_rate / 100.0) * \ self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item - return flt(current_tax_amount, self.precision("tax_amount", tax.parentfield)) + return flt(current_tax_amount, self.precision("tax_amount", tax)) def _get_tax_rate(self, tax, item_tax_map): if item_tax_map.has_key(tax.account_head): - return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax.parentfield)) + return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax)) else: return tax.rate @@ -355,7 +355,7 @@ class BuyingController(StockController): if tax.category in ["Valuation", "Valuation and Total"] and \ item.item_code in self.stock_items: item.item_tax_amount += flt(current_tax_amount, - self.precision("item_tax_amount", item.parentfield)) + self.precision("item_tax_amount", item)) # update valuation rate def update_valuation_rate(self, parentfield): @@ -363,21 +363,17 @@ class BuyingController(StockController): item.conversion_factor = item.conversion_factor or flt(webnotes.conn.get_value( "UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom}, "conversion_factor")) or 1 + if item.item_code and item.qty: - if self.doc.doctype == "Purchase Invoice": - purchase_rate = flt(item.rate, self.precision("rate", item.parentfield)) - else: - purchase_rate = flt(item.purchase_rate, self.precision("purchase_rate", item.parentfield)) - + self.round_floats_in(item) + + purchase_rate = item.rate if self.doc.doctype == "Purchase Invoice" else item.purchase_rate + # if no item code, which is sometimes the case in purchase invoice, # then it is not possible to track valuation against it - item.valuation_rate = flt( - (purchase_rate + \ - (flt(item.item_tax_amount, self.precision("item_tax_amount", item.parentfield)) + - flt(item.rm_supp_cost, self.precision("rm_supp_cost", item.parentfield)) - ) / flt(item.qty, self.precision("qty", item.parentfield)) - ) / flt(item.conversion_factor, self.precision("conversion_factor", item.parentfield)), - self.precision("valuation_rate", item.parentfield)) + item.valuation_rate = flt((purchase_rate + + (item.item_tax_amount + item.rm_supp_cost) / item.qty) / item.conversion_factor, + self.precision("valuation_rate", item)) else: item.valuation_rate = 0.0 diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 020e2af315..36d9d8ed7c 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -92,7 +92,11 @@ class SellingController(StockController): self.calculate_taxes() self.calculate_totals() # self.calculate_outstanding_amount() - # + + # TODO + # allocated amount of sales person + # total commission + self._cleanup() def determin_exclusive_rate(self): @@ -122,12 +126,12 @@ class SellingController(StockController): if cumulated_tax_fraction: item.basic_rate = flt((item.export_rate * self.doc.conversion_rate) / - (1 + cumulated_tax_fraction), self.precision("basic_rate", item.parentfield)) + (1 + cumulated_tax_fraction), self.precision("basic_rate", item)) - item.amount = flt(item.basic_rate * item.qty, self.precision("amount", item.parentfield)) + item.amount = flt(item.basic_rate * item.qty, self.precision("amount", item)) item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)), - self.precision("base_ref_rate", item.parentfield)) + self.precision("base_ref_rate", item)) def get_current_tax_fraction(self, tax, item_tax_map): """ @@ -156,11 +160,11 @@ class SellingController(StockController): def _set_base(item, print_field, base_field): """set values in base currency""" item.fields[base_field] = flt((flt(item.fields[print_field], - self.precision(print_field, item.parentfield)) * self.doc.conversion_rate), - self.precision(base_field, item.parentfield)) + self.precision(print_field, item)) * self.doc.conversion_rate), + self.precision(base_field, item)) for item in self.item_doclist: - self.round_floats_in_doc(item, item.parentfield) + self.round_floats_in(item) if item.adj_rate == 100: item.ref_rate = item.ref_rate or item.export_rate @@ -168,14 +172,14 @@ class SellingController(StockController): else: if item.ref_rate: item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)), - self.precision("export_rate", item.parentfield)) + self.precision("export_rate", item)) else: # assume that print rate and discount are specified item.ref_rate = flt(item.export_rate / (1.0 - (item.adj_rate / 100.0)), - self.precision("ref_rate", item.parentfield)) + self.precision("ref_rate", item)) item.export_amount = flt(item.export_rate * item.qty, - self.precision("export_amount", item.parentfield)) + self.precision("export_amount", item)) _set_base(item, "ref_rate", "base_ref_rate") _set_base(item, "export_rate", "basic_rate") @@ -189,7 +193,7 @@ class SellingController(StockController): tax.item_wise_tax_detail = {} self.validate_on_previous_row(tax) self.validate_inclusive_tax(tax) - self.round_floats_in_doc(tax, tax.parentfield) + self.round_floats_in(tax) def calculate_net_total(self): self.doc.net_total = 0 @@ -216,7 +220,7 @@ class SellingController(StockController): # and tax.grand_total_for_current_item for the first such iteration if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ tax.charge_type=="Actual": - zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax.parentfield)) + zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax)) current_tax_amount += zero_net_total_adjustment # store tax_amount for current item as it will be used for @@ -231,12 +235,12 @@ class SellingController(StockController): # item's amount, previously applied tax and the current tax on that item if i==0: tax.grand_total_for_current_item = flt(item.amount + - current_tax_amount, self.precision("total", tax.parentfield)) + current_tax_amount, self.precision("total", tax)) else: tax.grand_total_for_current_item = \ flt(self.tax_doclist[i-1].grand_total_for_current_item + - current_tax_amount, self.precision("total", tax.parentfield)) + current_tax_amount, self.precision("total", tax)) # in tax.total, accumulate grand total of each item tax.total += tax.grand_total_for_current_item @@ -259,7 +263,7 @@ class SellingController(StockController): if tax.charge_type == "Actual": # distribute the tax amount proportionally to each item row - actual = flt(tax.rate, self.precision("tax_amount", tax.parentfield)) + actual = flt(tax.rate, self.precision("tax_amount", tax)) current_tax_amount = (self.doc.net_total and ((item.amount / self.doc.net_total) * actual) or 0) @@ -272,7 +276,7 @@ class SellingController(StockController): current_tax_amount = (tax_rate / 100.0) * \ self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item - return flt(current_tax_amount, self.precision("tax_amount", tax.parentfield)) + return flt(current_tax_amount, self.precision("tax_amount", tax)) def validate_on_previous_row(self, tax): """ @@ -334,7 +338,7 @@ class SellingController(StockController): def _get_tax_rate(self, tax, item_tax_map): if item_tax_map.has_key(tax.account_head): - return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax.parentfield)) + return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax)) else: return tax.rate diff --git a/public/js/utils.js b/public/js/utils.js index 4df9555ae9..0284670761 100644 --- a/public/js/utils.js +++ b/public/js/utils.js @@ -13,8 +13,7 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . - -wn.provide('erpnext.utils'); +wn.provide("erpnext.utils"); erpnext.get_currency = function(company) { if(!company && cur_frm) @@ -23,4 +22,4 @@ erpnext.get_currency = function(company) { return wn.model.get(":Company", company).default_currency || wn.boot.sysdefaults.currency; else return wn.boot.sysdefaults.currency; -} \ No newline at end of file +} From c99853c8d4fbf9822f547d11410a368e6a285616 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 14 May 2013 13:35:09 +0530 Subject: [PATCH 07/17] [buying] [calculation] fix in calculate outstanding amount --- controllers/buying_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 30c3dbbefa..209bc60b1b 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -274,7 +274,7 @@ class BuyingController(StockController): self.doc.rounded_total_import = round(self.doc.grand_total_import) def calculate_outstanding_amount(self): - if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus == 0: + if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus < 2: self.doc.total_advance = flt(self.doc.total_advance, self.precision("total_advance")) self.doc.total_amount_to_pay = flt(self.doc.grand_total - flt(self.doc.write_off_amount, From 1dde46aff00b2a401447219d09d350c650c46fe3 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 15 May 2013 21:15:57 +0530 Subject: [PATCH 08/17] [buying, selling] [refactor] get item details --- .../purchase_common/purchase_common.js | 56 ++++++--- buying/utils.py | 83 +++++++++----- controllers/buying_controller.py | 30 +---- controllers/selling_controller.py | 7 ++ selling/doctype/quotation/quotation.py | 2 + selling/doctype/sales_common/sales_common.js | 107 +++++++++++++----- selling/doctype/sales_order/sales_order.py | 2 + selling/utils.py | 101 ++++++++++++++++- setup/utils.py | 2 +- utilities/transaction_base.py | 41 ++++++- 10 files changed, 327 insertions(+), 104 deletions(-) diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index dacee80e55..ff875bae64 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -83,26 +83,48 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ item_code: function(doc, cdt, cdn) { var me = this; - var item = locals[cdt][cdn]; + var item = wn.model.get_doc(cdt, cdn); + // validate company if(item.item_code) { - this.frm.call({ - method: "buying.utils.get_item_details", - child: item, - args: { - args: { - doctype: me.frm.doc.doctype, - docname: me.frm.doc.name, - item_code: item.item_code, - warehouse: item.warehouse, - supplier: me.frm.doc.supplier, - conversion_rate: me.frm.doc.conversion_rate, - price_list_name: me.frm.doc.price_list_name, - price_list_currency: me.frm.doc.price_list_currency, - plc_conversion_rate: me.frm.doc.plc_conversion_rate - } - }, + var fetch = true; + $.each(["company", "supplier"], function(i, fieldname) { + if(!me.frm.doc[fieldname]) { + fetch = false; + msgprint(wn._("Please specify") + ": " + + wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + ". " + wn._("It is needed to fetch Item Details.")); + } }); + + if(!fetch) { + item.item_code = null; + refresh_field("item_code", item.name, item.parentfield); + } else { + this.frm.call({ + method: "buying.utils.get_item_details", + child: item, + args: { + args: { + item_code: item.item_code, + warehouse: item.warehouse, + doctype: me.frm.doc.doctype, + docname: me.frm.doc.name, + supplier: me.frm.doc.supplier, + conversion_rate: me.frm.doc.conversion_rate, + price_list_name: me.frm.doc.price_list_name, + price_list_currency: me.frm.doc.price_list_currency, + plc_conversion_rate: me.frm.doc.plc_conversion_rate, + is_subcontracted: me.frm.doc.is_subcontracted, + company: me.frm.doc.company, + currency: me.frm.doc.currency + } + }, + callback: function(r) { + // TODO: calculate + } + }); + } } }, diff --git a/buying/utils.py b/buying/utils.py index 0431e642cb..54197b49fb 100644 --- a/buying/utils.py +++ b/buying/utils.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ from webnotes.utils import getdate, flt, add_days import json @@ -29,7 +30,11 @@ def get_item_details(args): "warehouse": None, "supplier": None, "transaction_date": None, - "conversion_rate": 1.0 + "conversion_rate": 1.0, + "price_list_name": None, + "price_list_currency": None, + "plc_conversion_rate": 1.0, + "is_subcontracted": "Yes" / "No" } """ if isinstance(args, basestring): @@ -37,36 +42,14 @@ def get_item_details(args): args = webnotes._dict(args) - item_wrapper = webnotes.bean("Item", args.item_code) - item = item_wrapper.doc + item_bean = webnotes.bean("Item", args.item_code) + item = item_bean.doc - from stock.utils import validate_end_of_life - validate_end_of_life(item.name, item.end_of_life) + _validate_item_details(args, item) - # fetch basic values - out = webnotes._dict() - out.update({ - "item_name": item.item_name, - "item_group": item.item_group, - "brand": item.brand, - "description": item.description, - "qty": 0, - "stock_uom": item.stock_uom, - "uom": item.stock_uom, - "conversion_factor": 1.0, - "warehouse": args.warehouse or item.default_warehouse, - "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in - item_wrapper.doclist.get({"parentfield": "item_tax"})))), - "batch_no": None, - "expense_head": item.purchase_account, - "cost_center": item.cost_center - }) + out = _get_basic_details(args, item_bean) - if args.supplier: - item_supplier = item_wrapper.doclist.get({"parentfield": "item_supplier_details", - "supplier": args.supplier}) - if item_supplier: - out["supplier_part_no"] = item_supplier[0].supplier_part_no + out.supplier_part_no = _get_supplier_part_no(args, item_bean) if out.warehouse: out.projected_qty = webnotes.conn.get_value("Bin", {"item_code": item.name, @@ -84,7 +67,7 @@ def get_item_details(args): "Supplier Quotation"]: # try fetching from price list if args.price_list_name and args.price_list_currency: - rates_as_per_price_list = get_rates_as_per_price_list(args, item_wrapper.doclist) + rates_as_per_price_list = get_rates_as_per_price_list(args, item_bean.doclist) if rates_as_per_price_list: out.update(rates_as_per_price_list) @@ -95,6 +78,33 @@ def get_item_details(args): out.update(last_purchase) return out + +def _get_basic_details(args, item_bean): + item = item_bean.doc + + out = webnotes._dict({ + "description": item.description_html or item.description, + "qty": 0.0, + "uom": item.stock_uom, + "conversion_factor": 1.0, + "warehouse": args.warehouse or item.default_warehouse, + "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in + item_bean.doclist.get({"parentfield": "item_tax"})))), + "batch_no": None, + "expense_head": item.purchase_account, + "cost_center": item.cost_center + }) + + for fieldname in ("item_name", "item_group", "brand", "stock_uom"): + out[fieldname] = item.fields.get(fieldname) + + return out + +def _get_supplier_part_no(args, item_bean): + item_supplier = item_bean.doclist.get({"parentfield": "item_supplier_details", + "supplier": args.supplier}) + + return item_supplier and item_supplier[0].supplier_part_no or None def get_rates_as_per_price_list(args, item_doclist=None): if not item_doclist: @@ -117,6 +127,21 @@ def get_rates_as_per_price_list(args, item_doclist=None): }) else: return webnotes._dict() + +def _validate_item_details(args, item): + from utilities.transaction_base import validate_item_fetch + validate_item_fetch(args, item) + + # validate if purchase item or subcontracted item + if item.is_purchase_item != "Yes": + msgprint(_("Item") + (" %s: " % item.name) + _("not a purchase item"), + raise_exception=True) + + if args.is_subcontracted == "Yes" and item.is_sub_contracted_item != "Yes": + msgprint(_("Item") + (" %s: " % item.name) + + _("not a sub-contracted item.") + + _("Please select a sub-contracted item or do not sub-contract the transaction."), + raise_exception=True) def get_last_purchase_details(item_code, doc_name, conversion_rate=1.0): """returns last purchase details in stock uom""" diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 209bc60b1b..1fc411fad4 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -22,6 +22,7 @@ import json from buying.utils import get_item_details from setup.utils import get_company_currency +from utilities.transaction_base import validate_conversion_rate from controllers.stock_controller import StockController @@ -32,12 +33,11 @@ class BuyingController(StockController): super(BuyingController, self).validate() self.validate_stock_or_nonstock_items() self.validate_warehouse_belongs_to_company() + if self.meta.get_field("currency"): self.company_currency = get_company_currency(self.doc.company) - self.validate_conversion_rate("currency", "conversion_rate") - - if self.doc.price_list_name and self.doc.price_list_currency: - self.validate_conversion_rate("price_list_currency", "plc_conversion_rate") + validate_conversion_rate(self.doc.currency, self.doc.conversion_rate, + self.meta.get_label("conversion_rate"), self.doc.company) # IMPORTANT: enable this only when client side code is similar to this one # self.calculate_taxes_and_totals() @@ -88,28 +88,6 @@ class BuyingController(StockController): if not item.fields.get(r): item.fields[r] = ret[r] - def validate_conversion_rate(self, currency_field, conversion_rate_field): - """common validation for currency and price list currency""" - - currency = self.doc.fields.get(currency_field) - conversion_rate = flt(self.doc.fields.get(conversion_rate_field)) - conversion_rate_label = self.meta.get_label(conversion_rate_field) - - if conversion_rate == 0: - msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True) - - # parenthesis for 'OR' are necessary as we want it to evaluate as - # mandatory valid condition and (1st optional valid condition - # or 2nd optional valid condition) - valid_conversion_rate = (conversion_rate and - ((currency == self.company_currency and conversion_rate == 1.00) - or (currency != self.company_currency and conversion_rate != 1.00))) - - if not valid_conversion_rate: - msgprint(_('Please enter valid ') + conversion_rate_label + (': ') - + ("1 %s = [?] %s" % (currency, self.company_currency)), - raise_exception=True) - def set_total_in_words(self): from webnotes.utils import money_in_words company_currency = get_company_currency(self.doc.company) diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 36d9d8ed7c..63b87e1eea 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -353,3 +353,10 @@ class SellingController(StockController): del tax.fields[fieldname] tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail) + + def validate_order_type(self): + valid_types = ["Sales", "Maintenance"] + if self.doc.order_type not in valid_types: + msgprint(_(self.meta.get_label("order_type")) + " " + + _("must be one of") + ": " + comma_or(valid_types), + raise_exception=True) diff --git a/selling/doctype/quotation/quotation.py b/selling/doctype/quotation/quotation.py index c154a6a3b0..7e83131204 100644 --- a/selling/doctype/quotation/quotation.py +++ b/selling/doctype/quotation/quotation.py @@ -142,6 +142,8 @@ class DocType(SellingController): #do not allow sales item in maintenance quotation and service item in sales quotation #----------------------------------------------------------------------------------------------- def validate_order_type(self): + super(DocType, self).validate_order_type() + if self.doc.order_type in ['Maintenance', 'Service']: for d in getlist(self.doclist, 'quotation_details'): is_service_item = sql("select is_service_item from `tabItem` where name=%s", d.item_code) diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js index 1d020e6780..67c7539b05 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -21,6 +21,83 @@ // cur_frm.cscript.other_fname - wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); fieldname // cur_frm.cscript.sales_team_fname - Sales Team fieldname +wn.provide("erpnext.selling"); + +erpnext.selling.SellingController = wn.ui.form.Controller.extend({ + setup: function() { + + }, + + refresh: function() { + + }, + + item_code: function(doc, cdt, cdn) { + var me = this; + var item = wn.model.get_doc(cdt, cdn); + if(item.item_code) { + var fetch = true; + $.each(["company", "customer"], function(i, fieldname) { + if(!me.frm.doc[fieldname]) { + fetch = false; + msgprint(wn._("Please specify") + ": " + + wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + ". " + wn._("It is needed to fetch Item Details.")); + } + }); + + if(!fetch) { + item.item_code = null; + refresh_field("item_code", item.name, item.parentfield); + } else { + this.frm.call({ + method: "selling.utils.get_item_details", + child: item, + args: { + args: { + item_code: item.item_code, + warehouse: item.warehouse, + doctype: me.frm.doc.doctype, + customer: me.frm.doc.customer, + currency: me.frm.doc.currency, + conversion_rate: me.frm.doc.conversion_rate, + price_list_name: me.frm.doc.price_list_name, + price_list_currency: me.frm.doc.price_list_currency, + plc_conversion_rate: me.frm.doc.plc_conversion_rate, + company: me.frm.doc.company, + order_type: me.frm.doc.order_type + + } + }, + callback: function(r) { + // TODO: calculate + } + }); + } + } + }, + + update_item_details: function() { + + }, + + set_dynamic_labels: function() { + + }, + + +}); + +// to save previous state of cur_frm.cscript +var prev_cscript = {}; +$.extend(prev_cscript, cur_frm.cscript); + +cur_frm.cscript = new erpnext.selling.SellingController({frm: cur_frm}); + +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, prev_cscript); + + // ============== Load Default Taxes =================== cur_frm.cscript.load_taxes = function(doc, cdt, cdn, callback) { // run if this is not executed from dt_map... @@ -264,7 +341,7 @@ cur_frm.cscript.price_list_name = function(doc, cdt, cdn) { // ******************** ITEM CODE ******************************** cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field("item_code").get_query = function(doc, cdt, cdn) { - if (inList(['Maintenance', 'Service'], doc.order_type)) { + if (doc.order_type == "Maintenance") { return erpnext.queries.item({ 'ifnull(tabItem.is_service_item, "No")': 'Yes' }); @@ -275,34 +352,6 @@ cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field("item_code").get_query } } - -cur_frm.cscript.item_code = function(doc, cdt, cdn) { - var fname = cur_frm.cscript.fname; - var d = locals[cdt][cdn]; - if (d.item_code) { - if (!doc.company) { - msgprint("Please select company to proceed"); - d.item_code = ''; - refresh_field('item_code', d.name, fname); - } else { - var callback = function(r, rt){ - cur_frm.cscript.recalc(doc, 1); - } - var args = { - 'item_code':d.item_code, - 'income_account':d.income_account, - 'cost_center': d.cost_center, - 'warehouse': d.warehouse - }; - get_server_fields('get_item_details',JSON.stringify(args), - fname,doc,cdt,cdn,1,callback); - } - } - if(cur_frm.cscript.custom_item_code){ - cur_frm.cscript.custom_item_code(doc, cdt, cdn); - } -} - //Barcode // cur_frm.cscript.barcode = function(doc, cdt, cdn) { diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py index 6a52e5a0fa..c74e7e1f9d 100644 --- a/selling/doctype/sales_order/sales_order.py +++ b/selling/doctype/sales_order/sales_order.py @@ -194,6 +194,8 @@ class DocType(SellingController): and current Sales Order""" % (self.doc.order_type, d.prevdoc_docname)) def validate_order_type(self): + super(DocType, self).validate_order_type() + #validate delivery date if self.doc.order_type == 'Sales' and not self.doc.delivery_date: msgprint("Please enter 'Expected Delivery Date'") diff --git a/selling/utils.py b/selling/utils.py index 21e94f7234..23574dfa9e 100644 --- a/selling/utils.py +++ b/selling/utils.py @@ -16,6 +16,9 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ +from webnotes.utils import flt +import json def get_customer_list(doctype, txt, searchfield, start, page_len, filters): if webnotes.conn.get_default("cust_master_name") == "Customer Name": @@ -29,4 +32,100 @@ def get_customer_list(doctype, txt, searchfield, start, page_len, filters): case when customer_name like %s then 0 else 1 end, name, customer_name limit %s, %s""" % (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), - ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) \ No newline at end of file + ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) + +@webnotes.whitelist() +def get_item_details(args): + """ + args = { + "item_code": "", + "warehouse": None, + "customer": "", + "conversion_rate": 1.0, + "price_list_name": None, + "price_list_currency": None, + "plc_conversion_rate": 1.0 + } + """ + if isinstance(args, basestring): + args = json.loads(args) + args = webnotes._dict(args) + + item_bean = webnotes.bean("Item", args.item_code) + + _validate_item_details(args, item_bean.doc) + + out = _get_basic_details(args, item_bean) + + if args.price_list_name and args.price_list_currency: + out.update(_get_price_list_rate(args, item_bean)) + + if out.warehouse or out.reserved_warehouse: + out.update(_get_available_qty(args, out.warehouse or out.reserved_warehouse)) + + out.customer_item_code = _get_customer_item_code(args, item_bean) + + return out + +def _validate_item_details(args, item): + from utilities.transaction_base import validate_item_fetch + validate_item_fetch(args, item) + + # validate if sales item or service item + if args.order_type == "Maintenance": + if item.is_service_item != "Yes": + msgprint(_("Item") + (" %s: " % item.name) + + _("not a service item.") + + _("Please select a service item or change the order type to Sales."), + raise_exception=True) + + elif item.is_sales_item != "Yes": + msgprint(_("Item") + (" %s: " % item.name) + _("not a sales item"), + raise_exception=True) + +def _get_basic_details(args, item_bean): + item = item_bean.doc + out = webnotes._dict({ + "description": item.description_html or item.description, + "reserved_warehouse": item.default_warehouse, + "warehouse": item.default_warehouse or args.warehouse, + "income_account": item.default_income_account or args.income_account, + "expense_account": item.purchase_account or args.expense_account, + "cost_center": item.default_sales_cost_center or args.cost_center, + "qty": 1.0, + "adj_rate": 0.0, + "export_amount": 0.0, + "amount": 0.0, + "batch_no": None, + "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in + item_bean.doclist.get({"parentfield": "item_tax"})))), + }) + + for fieldname in ("item_name", "item_group", "barcode", "brand", "stock_uom"): + out[fieldname] = item.fields.get(fieldname) + + return out + +def _get_price_list_rate(args, item_bean): + base_ref_rate = item_bean.doclist.get({ + "parentfield": "ref_rate_details", + "price_list_name": args.price_list_name, + "price_list_currency": args.price_list_currency, + "selling": 1}) + out = webnotes._dict() + out.base_ref_rate = flt(base_ref_rate[0].ref_rate) if base_ref_rate else 0.0 + out.basic_rate = out.base_ref_rate + out.ref_rate = out.base_ref_rate / flt(args.conversion_rate) + out.export_rate = out.ref_rate + return out + +def _get_available_qty(args, warehouse): + return webnotes.conn.get_value("Bin", {"item_code": args.item_code, "warehouse": warehouse}, + ["projected_qty", "actual_qty"], as_dict=True) or {} + +def _get_customer_item_code(args, item_bean): + customer_item_code = item_bean.doclist.get({"parentfield": "item_customer_details", + "customer_name": args.customer}) + + return customer_item_code and customer_item_code[0].ref_code or None + \ No newline at end of file diff --git a/setup/utils.py b/setup/utils.py index 1a86921692..33fa3e286e 100644 --- a/setup/utils.py +++ b/setup/utils.py @@ -46,4 +46,4 @@ def get_price_list_currency(args): if result and len(result)==1: return {"price_list_currency": result[0][0]} else: - return {} \ No newline at end of file + return {} diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 5d7d1a84b1..540b385d51 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ from webnotes.utils import load_json, cstr, flt, now_datetime from webnotes.model.doc import addchild @@ -268,4 +269,42 @@ class TransactionBase(DocListController): def validate_posting_time(self): if not self.doc.posting_time: self.doc.posting_time = now_datetime().strftime('%H:%M:%S') - \ No newline at end of file + +def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company): + """common validation for currency and price list currency""" + if conversion_rate == 0: + msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True) + + company_currency = webnotes.conn.get_value("Company", company, "default_currency") + + # parenthesis for 'OR' are necessary as we want it to evaluate as + # mandatory valid condition and (1st optional valid condition + # or 2nd optional valid condition) + valid_conversion_rate = (conversion_rate and + ((currency == company_currency and conversion_rate == 1.00) + or (currency != company_currency and conversion_rate != 1.00))) + + if not valid_conversion_rate: + msgprint(_('Please enter valid ') + conversion_rate_label + (': ') + + ("1 %s = [?] %s" % (currency, company_currency)), + raise_exception=True) + +def validate_item_fetch(args, item): + from stock.utils import validate_end_of_life + validate_end_of_life(item.name, item.end_of_life) + + # validate company + if not args.company: + msgprint(_("Please specify Company"), raise_exception=True) + + # validate conversion rates + meta = webnotes.get_doctype(args.doctype) + if meta.get_field("currency"): + # validate conversion rate + validate_conversion_rate(args.currency, args.conversion_rate, + meta.get_label("conversion_rate"), args.company) + + # validate price list conversion rate + if args.price_list_name and args.price_list_currency: + validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate, + meta.get_label("plc_conversion_rate"), args.company) \ No newline at end of file From f309613a7d4d5befc880e5d499d7564dd7c8e1c1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 21 May 2013 19:35:06 +0530 Subject: [PATCH 09/17] [selling] [calculations] client side calculations, cleanup, patch fixes --- .../purchase_invoice/purchase_invoice.js | 4 +- .../doctype/sales_invoice/sales_invoice.js | 189 +-- .../doctype/sales_invoice/sales_invoice.py | 128 +- .../doctype/sales_invoice/sales_invoice.txt | 18 +- .../sales_invoice_item/sales_invoice_item.txt | 8 +- .../purchase_common/purchase_common.js | 27 +- buying/utils.py | 69 +- controllers/selling_controller.py | 197 ++- .../may_2013/p01_selling_net_total_export.py | 23 +- selling/doctype/quotation/quotation.py | 11 - selling/doctype/quotation/quotation.txt | 16 +- .../doctype/quotation_item/quotation_item.txt | 6 +- selling/doctype/sales_common/sales_common.js | 1321 ++++++++--------- selling/doctype/sales_common/sales_common.py | 313 ++-- selling/doctype/sales_order/sales_order.py | 7 - selling/doctype/sales_order/sales_order.txt | 18 +- .../sales_order_item/sales_order_item.txt | 6 +- selling/doctype/sales_team/sales_team.txt | 10 +- selling/utils.py | 69 +- stock/doctype/delivery_note/delivery_note.py | 11 - stock/doctype/delivery_note/delivery_note.txt | 16 +- .../delivery_note_item/delivery_note_item.txt | 8 +- utilities/transaction_base.py | 17 +- 23 files changed, 1174 insertions(+), 1318 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index 92f17487bc..b498ce25af 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -54,10 +54,8 @@ erpnext.buying.PurchaseInvoiceController = erpnext.buying.BuyingController.exten } }); -var new_cscript = new erpnext.buying.PurchaseInvoiceController({frm: cur_frm}); - // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new_cscript); +$.extend(cur_frm.cscript, new erpnext.buying.PurchaseInvoiceController({frm: cur_frm})); cur_frm.cscript.onload = function(doc,dt,dn) { diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index be6ec3d001..800cd2bba5 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -26,48 +26,62 @@ wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); -// On Load -// ------- -cur_frm.cscript.onload = function(doc,dt,dn) { - cur_frm.cscript.manage_rounded_total(); - if(!doc.customer && doc.debit_to) wn.meta.get_docfield(dt, 'debit_to', dn).print_hide = 0; - if (doc.__islocal) { - if(!doc.due_date) set_multiple(dt,dn,{due_date:get_today()}); - if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()}); - if(!doc.currency && sys_defaults.currency) set_multiple(dt,dn,{currency:sys_defaults.currency}); - if(!doc.price_list_currency) set_multiple(dt, dn, {price_list_currency: doc.currency, plc_conversion_rate: 1}); +erpnext.selling.SalesInvoiceController = erpnext.selling.SellingController.extend({ + onload: function() { + this._super(); + + // show debit_to in print format + if(!this.frm.doc.customer && this.frm.doc.debit_to) { + this.frm.set_df_property("debit_to", "print_hide", 0); + } + }, + + refresh: function(doc, dt, dn) { + this._super(); + + cur_frm.cscript.is_opening(doc, dt, dn); - } -} + // Show / Hide button + cur_frm.clear_custom_buttons(); + // if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, dt, dn); -cur_frm.cscript.onload_post_render = function(doc, dt, dn) { - var callback = function(doc, dt, dn) { - // called from mapper, update the account names for items and customer - var callback2 = function(doc, dt, dn) { - if(doc.customer && doc.__islocal) { - $c_obj(make_doclist(doc.doctype,doc.name), - 'load_default_accounts','', - function(r,rt) { - refresh_field('entries'); - cur_frm.cscript.customer(doc,dt,dn,onload=true); - } - ); + if(doc.docstatus==1) { + cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry); + cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + + if(doc.is_pos==1 && doc.update_stock!=1) + cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']); + + if(doc.outstanding_amount!=0) + cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher); + } + cur_frm.cscript.hide_fields(doc, dt, dn); + }, + + is_pos: function() { + if(cint(this.frm.doc.is_pos)) { + if(!this.frm.doc.company) { + this.frm.set_value("is_pos", 0); + msgprint(wn._("Please specify Company to proceed")); + } else { + var me = this; + this.frm.call({ + doc: me.frm.doc, + method: "set_missing_values", + }); } } - // defined in sales_common.js - var callback1 = function(doc, dt, dn) { - //for previously created sales invoice, set required field related to pos - cur_frm.cscript.update_item_details(doc, dt, dn, callback2); - } - if(doc.is_pos ==1) cur_frm.cscript.is_pos(doc, dt, dn,callback1); - else cur_frm.cscript.update_item_details(doc, dt, dn, callback2); - } - - cur_frm.cscript.hide_price_list_currency(doc, dt, dn, callback); - -} + // TODO toggle display of fields + }, + + debit_to: function() { + this.customer(); + }, +}); +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, new erpnext.selling.SalesInvoiceController({frm: cur_frm})); // Hide Fields // ------------ @@ -104,50 +118,6 @@ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) { } -// Refresh -// ------- -cur_frm.cscript.refresh = function(doc, dt, dn) { - cur_frm.cscript.is_opening(doc, dt, dn); - erpnext.hide_naming_series(); - - // Show / Hide button - cur_frm.clear_custom_buttons(); - if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, dt, dn); - - if(doc.docstatus==1) { - cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry); - cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); - - if(doc.is_pos==1 && doc.update_stock!=1) - cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']); - - if(doc.outstanding_amount!=0) - cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher); - } - cur_frm.cscript.hide_fields(doc, dt, dn); - -} - -//fetch retail transaction related fields -//-------------------------------------------- -cur_frm.cscript.is_pos = function(doc,dt,dn,callback){ - cur_frm.cscript.hide_fields(doc, dt, dn); - if(doc.is_pos == 1){ - if (!doc.company) { - msgprint("Please select company to proceed"); - doc.is_pos = 0; - refresh_field('is_pos'); - } - else { - var callback1 = function(r,rt){ - if(callback) callback(doc, dt, dn); - cur_frm.refresh(); - } - $c_obj(make_doclist(dt,dn),'set_pos_fields','',callback1); - } - } -} - cur_frm.cscript.mode_of_payment = function(doc) { cur_frm.call({ method: "get_bank_cash_account", @@ -168,66 +138,10 @@ cur_frm.cscript.warehouse = function(doc, cdt , cdn) { } } - - -//Customer -cur_frm.cscript.customer = function(doc,dt,dn,onload) { - cur_frm.toggle_display("contact_section", doc.customer); - - var pl = doc.price_list_name; - var callback = function(r,rt) { - var callback2 = function(doc, dt, dn) { - doc = locals[dt][dn]; - if(doc.debit_to && doc.posting_date){ - get_server_fields('get_cust_and_due_date','','',doc,dt,dn,1, - function(doc, dt, dn) { - cur_frm.refresh(); - if (!onload && (pl != doc.price_list_name)) cur_frm.cscript.price_list_name(doc, dt, dn); - }); - - } - } - var doc = locals[cur_frm.doctype][cur_frm.docname]; - get_server_fields('get_debit_to','','',doc, dt, dn, 0, callback2); - } - var args = onload ? 'onload':'' - if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', args, callback); - -} - - - cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); } -// Set Due Date = posting date + credit days -cur_frm.cscript.debit_to = function(doc,dt,dn) { - - var callback2 = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - cur_frm.refresh(); - } - - var callback = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - if(doc.customer) $c_obj(make_doclist(dt,dn), 'get_default_customer_address', '', callback2); - cur_frm.toggle_display("contact_section", doc.customer); - - cur_frm.refresh(); - } - - if(doc.debit_to && doc.posting_date){ - get_server_fields('get_cust_and_due_date','','',doc,dt,dn,1,callback); - } -} - - - -//refresh advance amount -//------------------------------------------------- - - cur_frm.cscript.write_off_outstanding_amount_automatically = function(doc) { if (doc.write_off_outstanding_amount_automatically == 1) doc.write_off_amount = flt(doc.grand_total) - flt(doc.paid_amount); @@ -244,9 +158,6 @@ cur_frm.cscript.write_off_amount = function(doc) { cur_frm.cscript.write_off_outstanding_amount_automatically(doc); } - -//Set debit and credit to zero on adding new row -//---------------------------------------------- cur_frm.fields_dict['entries'].grid.onrowadd = function(doc, cdt, cdn){ cl = getchildren('Sales Invoice Item', doc.name, cur_frm.cscript.fname, doc.doctype); diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 615da19a70..e58b4000f2 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -41,7 +41,6 @@ class DocType(SellingController): def validate(self): super(DocType, self).validate() - self.fetch_missing_values() self.validate_posting_time() self.so_dn_required() self.validate_proj_cust() @@ -50,7 +49,6 @@ class DocType(SellingController): sales_com_obj.check_active_sales_items(self) sales_com_obj.check_conversion_rate(self) sales_com_obj.validate_max_discount(self, 'entries') - sales_com_obj.get_allocated_sum(self) sales_com_obj.validate_fiscal_year(self.doc.fiscal_year, self.doc.posting_date,'Posting Date') self.validate_customer() @@ -134,25 +132,16 @@ class DocType(SellingController): self.validate_recurring_invoice() self.convert_to_recurring() - def fetch_missing_values(self): - # fetch contact and address details for customer, if they are not mentioned - if not (self.doc.contact_person and self.doc.customer_address): - for fieldname, val in self.get_default_address_and_contact("customer").items(): - if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): - self.doc.fields[fieldname] = val - - # fetch missing item values - for item in self.doclist.get({"parentfield": "entries"}): - if item.fields.get("item_code"): - ret = get_obj('Sales Common').get_item_details(item.fields, self) - for fieldname, value in ret.items(): - if self.meta.get_field(fieldname, parentfield="entries") and \ - not item.fields.get(fieldname): - item.fields[fieldname] = value + def set_missing_values(self, for_validate=False): + super(DocType, self).set_missing_values(for_validate) + self.set_pos_fields(for_validate) - # fetch pos details, if they are not fetched - if cint(self.doc.is_pos): - self.set_pos_fields(for_validate=True) + def set_customer_defaults(self): + # TODO cleanup these methods + self.doc.fields.update(self.get_debit_to()) + self.get_cust_and_due_date() + + super(DocType, self).set_customer_defaults() def update_time_log_batch(self, sales_invoice): for d in self.doclist.get({"doctype":"Sales Invoice Item"}): @@ -173,10 +162,11 @@ class DocType(SellingController): """Set retail related fields from pos settings""" if cint(self.doc.is_pos) != 1: return + + from selling.utils import get_pos_settings, apply_pos_settings + pos = get_pos_settings(self.doc.company) - if self.pos_settings: - pos = self.pos_settings[0] - + if pos: self.doc.conversion_rate = flt(pos.conversion_rate) if not self.doc.debit_to: @@ -193,7 +183,7 @@ class DocType(SellingController): # set pos values in items for item in self.doclist.get({"parentfield": "entries"}): if item.fields.get('item_code'): - for fieldname, val in self.apply_pos_settings(item.fields).items(): + for fieldname, val in apply_pos_settings(pos, item.fields).items(): if (not for_validate) or (for_validate and not item.fields.get(fieldname)): item.fields[fieldname] = val @@ -203,7 +193,7 @@ class DocType(SellingController): # fetch charges if self.doc.charge and not len(self.doclist.get({"parentfield": "other_charges"})): - self.get_other_charges() + self.set_taxes() def get_customer_account(self): """Get Account Head to which amount needs to be Debited based on Customer""" @@ -272,86 +262,14 @@ class DocType(SellingController): ret = self.get_debit_to() self.doc.debit_to = ret.get('debit_to') - - def load_default_accounts(self): - """ - Loads default accounts from items, customer when called from mapper - """ - self.get_income_expense_account('entries') - - - def get_income_expense_account(self,doctype): - for d in getlist(self.doclist, doctype): - if d.item_code: - item = webnotes.conn.get_value("Item", d.item_code, ["default_income_account", - "default_sales_cost_center", "purchase_account"], as_dict=True) - d.income_account = item['default_income_account'] or "" - d.cost_center = item['default_sales_cost_center'] or "" - - if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \ - and cint(self.doc.is_pos) and cint(self.doc.update_stock): - d.expense_account = item['purchase_account'] or "" - - def get_item_details(self, args=None): - import json - args = args and json.loads(args) or {} - if args.get('item_code'): - ret = get_obj('Sales Common').get_item_details(args, self) - - if cint(self.doc.is_pos) == 1 and self.pos_settings: - ret = self.apply_pos_settings(args, ret) - - return ret - - elif cint(self.doc.is_pos) == 1 and self.pos_settings: - for doc in self.doclist.get({"parentfield": "entries"}): - if doc.fields.get('item_code'): - ret = self.apply_pos_settings(doc.fields) - for r in ret: - if not doc.fields.get(r): - doc.fields[r] = ret[r] - @property def pos_settings(self): if not hasattr(self, "_pos_settings"): - dtl = webnotes.conn.sql("""select * from `tabPOS Setting` where user = %s - and company = %s""", (webnotes.session['user'], self.doc.company), as_dict=1) - if not dtl: - dtl = webnotes.conn.sql("""select * from `tabPOS Setting` - where ifnull(user,'') = '' and company = %s""", self.doc.company, as_dict=1) - self._pos_settings = dtl + from selling.utils import get_pos_settings + self._pos_settings = get_pos_settings({"company": self.doc.company}) return self._pos_settings - def apply_pos_settings(self, args, ret=None): - if not ret: ret = {} - - pos = self.pos_settings[0] - - item = webnotes.conn.sql("""select default_income_account, default_sales_cost_center, - default_warehouse, purchase_account from tabItem where name = %s""", - args.get('item_code'), as_dict=1) - - if item: - item = item[0] - - ret.update({ - "income_account": item.get("default_income_account") \ - or pos.get("income_account") or args.get("income_account"), - "cost_center": item.get("default_sales_cost_center") \ - or pos.get("cost_center") or args.get("cost_center"), - "warehouse": item.get("default_warehouse") \ - or pos.get("warehouse") or args.get("warehouse"), - "expense_account": item.get("purchase_account") \ - or pos.get("expense_account") or args.get("expense_account") - }) - - if ret.get("warehouse"): - ret["actual_qty"] = flt(webnotes.conn.get_value("Bin", - {"item_code": args.get("item_code"), "warehouse": args.get("warehouse")}, - "actual_qty")) - return ret - def get_barcode_details(self, barcode): return get_obj('Sales Common').get_barcode_details(barcode) @@ -375,14 +293,6 @@ class DocType(SellingController): return get_obj('Sales Common').get_tc_details(self) - def load_default_taxes(self): - self.doclist = get_obj('Sales Common').load_default_taxes(self) - - - def get_other_charges(self): - self.doclist = get_obj('Sales Common').get_other_charges(self) - - def get_advances(self): super(DocType, self).get_advances(self.doc.debit_to, "Sales Invoice Advance", "advance_adjustment_details", "credit") @@ -611,7 +521,9 @@ class DocType(SellingController): else: self.doclist = self.doc.clear_table(self.doclist, 'packing_details') webnotes.conn.set(self.doc,'paid_amount',0) - + + # TODO + # move to calculations webnotes.conn.set(self.doc, 'outstanding_amount', flt(self.doc.grand_total) - flt(self.doc.total_advance) - flt(self.doc.paid_amount) - flt(self.doc.write_off_amount)) diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt index f1c0cab74d..8b114e7ffb 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.txt +++ b/accounts/doctype/sales_invoice/sales_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-06 12:03:41", + "creation": "2013-05-21 16:16:41", "docstatus": 0, - "modified": "2013-05-09 17:34:14", + "modified": "2013-05-21 18:25:07", "modified_by": "Administrator", "owner": "Administrator" }, @@ -224,11 +224,12 @@ "doctype": "DocField", "fieldname": "entries", "fieldtype": "Table", - "label": "Entries", + "label": "Sales Invoice Items", "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Sales Invoice Item", - "read_only": 0 + "read_only": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -460,6 +461,15 @@ "print_hide": 1, "read_only": 1 }, + { + "doctype": "DocField", + "fieldname": "other_charges_total_export", + "fieldtype": "Currency", + "label": "Total Taxes and Charges (Export)", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "other_charges_calculation", diff --git a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt index 2a6384d762..4dcd901f28 100644 --- a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt +++ b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-10 08:35:44", + "creation": "2013-04-19 13:30:26", "docstatus": 0, - "modified": "2013-04-17 14:05:20", + "modified": "2013-05-21 16:43:21", "modified_by": "Administrator", "owner": "Administrator" }, @@ -107,7 +107,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 0, + "read_only": 1, "reqd": 0 }, { @@ -163,7 +163,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 0, + "read_only": 1, "reqd": 1, "search_index": 0 }, diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index ff875bae64..fa2138afdc 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -84,20 +84,8 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ item_code: function(doc, cdt, cdn) { var me = this; var item = wn.model.get_doc(cdt, cdn); - - // validate company if(item.item_code) { - var fetch = true; - $.each(["company", "supplier"], function(i, fieldname) { - if(!me.frm.doc[fieldname]) { - fetch = false; - msgprint(wn._("Please specify") + ": " + - wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + - ". " + wn._("It is needed to fetch Item Details.")); - } - }); - - if(!fetch) { + if(!this.validate_company_and_party()) { item.item_code = null; refresh_field("item_code", item.name, item.parentfield); } else { @@ -128,6 +116,19 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ } }, + validate_company_and_party: function() { + var valid = true; + $.each(["company", "supplier"], function(i, fieldname) { + if(!me.frm.doc[fieldname]) { + valid = false; + msgprint(wn._("Please specify") + ": " + + wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + ". " + wn._("It is needed to fetch Item Details.")); + } + }); + return valid; + }, + update_item_details: function(doc, dt, dn, callback) { if(!this.frm.doc.__islocal) return; diff --git a/buying/utils.py b/buying/utils.py index 54197b49fb..952d70a602 100644 --- a/buying/utils.py +++ b/buying/utils.py @@ -59,23 +59,12 @@ def get_item_details(args): out.schedule_date = out.lead_time_date = add_days(args.transaction_date, item.lead_time_days) - # set zero - out.purchase_ref_rate = out.discount_rate = out.purchase_rate = \ - out.import_ref_rate = out.import_rate = 0.0 + meta = webnotes.get_doctype(args.doctype) - if args.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", - "Supplier Quotation"]: - # try fetching from price list - if args.price_list_name and args.price_list_currency: - rates_as_per_price_list = get_rates_as_per_price_list(args, item_bean.doclist) - if rates_as_per_price_list: - out.update(rates_as_per_price_list) - - # if not found, fetch from last purchase transaction - if not out.purchase_rate: - last_purchase = get_last_purchase_details(item.name, args.docname, args.conversion_rate) - if last_purchase: - out.update(last_purchase) + if meta.get_field("currency"): + out.purchase_ref_rate = out.discount_rate = out.purchase_rate = \ + out.import_ref_rate = out.import_rate = 0.0 + out.update(_get_price_list_rate(args, item_bean, meta)) return out @@ -100,34 +89,38 @@ def _get_basic_details(args, item_bean): return out +def _get_price_list_rate(args, item_bean, meta=None): + from utilities.transaction_base import validate_currency + item = item_bean.doc + out = webnotes._dict() + + # try fetching from price list + if args.price_list_name and args.price_list_currency: + price_list_rate = item_bean.doclist.get({ + "parentfield": "ref_rate_details", + "price_list_name": args.price_list_name, + "ref_currency": args.price_list_currency, + "buying": 1}) + if price_list_rate: + out.purchase_ref_rate = flt(price_list_rate[0].ref_rate) * flt(args.plc_conversion_rate) + + # if not found, fetch from last purchase transaction + if not out.purchase_ref_rate: + last_purchase = get_last_purchase_details(item.name, args.docname, args.conversion_rate) + if last_purchase: + out.update(last_purchase) + + if out.purchase_ref_rate or out.purchase_rate or out.rate: + validate_currency(args, item, meta) + + return out + def _get_supplier_part_no(args, item_bean): item_supplier = item_bean.doclist.get({"parentfield": "item_supplier_details", "supplier": args.supplier}) return item_supplier and item_supplier[0].supplier_part_no or None -def get_rates_as_per_price_list(args, item_doclist=None): - if not item_doclist: - item_doclist = webnotes.bean("Item", args.item_code).doclist - - result = item_doclist.get({"parentfield": "ref_rate_details", - "price_list_name": args.price_list_name, "ref_currency": args.price_list_currency, - "buying": 1}) - - if result: - purchase_ref_rate = flt(result[0].ref_rate) * flt(args.plc_conversion_rate) - conversion_rate = flt(args.conversion_rate) or 1.0 - return webnotes._dict({ - "purchase_ref_rate": purchase_ref_rate, - "purchase_rate": purchase_ref_rate, - "rate": purchase_ref_rate, - "discount_rate": 0, - "import_ref_rate": purchase_ref_rate / conversion_rate, - "import_rate": purchase_ref_rate / conversion_rate - }) - else: - return webnotes._dict() - def _validate_item_details(args, item): from utilities.transaction_base import validate_item_fetch validate_item_fetch(args, item) diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 63b87e1eea..a92ff88c48 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -16,7 +16,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, flt +from webnotes.utils import cint, flt, comma_or from setup.utils import get_company_currency from webnotes import msgprint, _ import json @@ -24,9 +24,73 @@ import json from controllers.stock_controller import StockController class SellingController(StockController): + def onload_post_render(self): + self.set_price_list_currency() + + # contact, address, item details and pos details (if applicable) + self.set_missing_values() + + if self.meta.get_field("other_charges"): + self.set_taxes() + def validate(self): super(SellingController, self).validate() + # self.calculate_taxes_and_totals() self.set_total_in_words() + self.set_missing_values(for_validate=True) + + def set_price_list_currency(self): + if self.doc.price_list_name and not self.doc.price_list_currency: + # TODO - change this, since price list now has only one currency allowed + from setup.utils import get_price_list_currency + self.doc.fields.update(get_price_list_currency( + {"price_list_name": self.doc.price_list_name, "use_for": "selling"})) + + def set_missing_values(self, for_validate=False): + # set contact and address details for customer, if they are not mentioned + if self.doc.customer and not (self.doc.contact_person and self.doc.customer_address): + for fieldname, val in self.get_default_address_and_contact("customer").items(): + if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): + self.doc.fields[fieldname] = val + + # set missing item values + from selling.utils import get_item_details + for item in self.doclist.get({"parentfield": "entries"}): + if item.fields.get("item_code"): + ret = get_item_details(item.fields) + for fieldname, value in ret.items(): + if self.meta.get_field(fieldname, parentfield="entries") and \ + not item.fields.get(fieldname): + item.fields[fieldname] = value + + def set_taxes(self): + if not self.doclist.get({"parentfield": "other_charges"}): + if not self.doc.charge: + # get the default tax master + self.doc.charge = webnotes.conn.get_value("Sales Taxes and Charges Master", + {"is_default": 1}) + + if self.doc.charge: + from webnotes.model import default_fields + tax_master = webnotes.bean("Sales Taxes and Charges Master", self.doc.charge) + for i, tax in enumerate(tax_master.doclist.get({"parentfield": "other_charges"})): + for fieldname in default_fields: + tax.fields[fieldname] = None + + tax.fields.update({ + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "idx": i+1 + }) + + self.doclist.append(tax) + + def get_other_charges(self): + self.doclist = self.doc.clear_table(self.doclist, "other_charges") + self.set_taxes() + + def set_customer_defaults(self): + self.get_default_customer_address() def set_total_in_words(self): from webnotes.utils import money_in_words @@ -81,25 +145,19 @@ class SellingController(StockController): self.calculate_item_values() self.initialize_taxes() - - self.determin_exclusive_rate() - - # TODO - # code: save net_total_export on client side - # print format: show net_total_export instead of net_total - + self.determine_exclusive_rate() self.calculate_net_total() self.calculate_taxes() self.calculate_totals() + self.calculate_commission() + self.calculate_contribution() # self.calculate_outstanding_amount() - - # TODO - # allocated amount of sales person - # total commission - self._cleanup() - def determin_exclusive_rate(self): + # TODO + # print format: show net_total_export instead of net_total + + def determine_exclusive_rate(self): if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)): # no inclusive tax return @@ -108,15 +166,10 @@ class SellingController(StockController): item_tax_map = self._load_item_tax_rate(item.item_tax_rate) cumulated_tax_fraction = 0 for i, tax in enumerate(self.tax_doclist): - if cint(tax.included_in_print_rate): - tax.tax_fraction_for_current_item = \ - self.get_current_tax_fraction(tax, item_tax_map) - else: - tax.tax_fraction_for_current_item = 0 - + tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax, item_tax_map) + if i==0: - tax.grand_total_fraction_for_current_item = 1 + \ - tax.tax_fraction_for_current_item + tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item else: tax.grand_total_fraction_for_current_item = \ self.tax_doclist[i-1].grand_total_fraction_for_current_item \ @@ -130,8 +183,12 @@ class SellingController(StockController): item.amount = flt(item.basic_rate * item.qty, self.precision("amount", item)) - item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)), - self.precision("base_ref_rate", item)) + if item.adj_rate == 100: + item.base_ref_rate = item.basic_rate + item.basic_rate = 0.0 + else: + item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)), + self.precision("base_ref_rate", item)) def get_current_tax_fraction(self, tax, item_tax_map): """ @@ -188,24 +245,23 @@ class SellingController(StockController): def initialize_taxes(self): for tax in self.tax_doclist: tax.tax_amount = tax.total = 0.0 + tax.item_wise_tax_detail = {} + # temporary fields tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0 - tax.item_wise_tax_detail = {} + self.validate_on_previous_row(tax) self.validate_inclusive_tax(tax) self.round_floats_in(tax) def calculate_net_total(self): - self.doc.net_total = 0 - self.doc.net_total_export = 0 + self.doc.net_total = self.doc.net_total_export = 0.0 for item in self.item_doclist: self.doc.net_total += item.amount self.doc.net_total_export += item.export_amount - - self.doc.net_total = flt(self.doc.net_total, self.precision("net_total")) - self.doc.net_total_export = flt(self.doc.net_total_export, - self.precision("net_total_export")) + + self.round_floats_in(self.doc, ["net_total", "net_total_export"]) def calculate_taxes(self): for item in self.item_doclist: @@ -218,8 +274,8 @@ class SellingController(StockController): # case when net total is 0 but there is an actual type charge # in this case add the actual amount to tax.tax_amount # and tax.grand_total_for_current_item for the first such iteration - if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ - tax.charge_type=="Actual": + if tax.charge_type=="Actual" and \ + not (current_tax_amount or self.doc.net_total or tax.tax_amount): zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax)) current_tax_amount += zero_net_total_adjustment @@ -228,7 +284,7 @@ class SellingController(StockController): tax.tax_amount_for_current_item = current_tax_amount # accumulate tax amount into tax.tax_amount - tax.tax_amount += tax.tax_amount_for_current_item + tax.tax_amount += current_tax_amount # Calculate tax.total viz. grand total till that step # note: grand_total_for_current_item contains the contribution of @@ -245,8 +301,7 @@ class SellingController(StockController): # in tax.total, accumulate grand total of each item tax.total += tax.grand_total_for_current_item - # store tax_breakup for each item - # DOUBT: should valuation type amount also be stored? + # store tax breakup for each item tax.item_wise_tax_detail[item.item_code] = current_tax_amount def calculate_totals(self): @@ -254,12 +309,43 @@ class SellingController(StockController): self.tax_doclist[-1].total or self.doc.net_total, self.precision("grand_total")) self.doc.grand_total_export = flt(self.doc.grand_total / self.doc.conversion_rate, self.precision("grand_total_export")) + + self.doc.other_charges_total = flt(self.doc.grand_total - self.doc.net_total, + self.precision("other_charges_total")) + self.doc.other_charges_total_export = flt(self.doc.grand_total_export - self.doc.net_total_export, + self.precision("other_charges_total_export")) self.doc.rounded_total = round(self.doc.grand_total) self.doc.rounded_total_export = round(self.doc.grand_total_export) + def calculate_commission(self): + if self.doc.commission_rate > 100: + msgprint(_(self.meta.get_label("commission_rate")) + " " + + _("cannot be greater than 100"), raise_exception=True) + + self.doc.total_commission = flt(self.doc.net_total * self.doc.commission_rate / 100.0, + self.precision("total_commission")) + + def calculate_contribution(self): + total = 0.0 + sales_team = self.doclist.get({"parentfield": "sales_team"}) + for sales_person in sales_team: + self.round_floats_in(sales_person) + + sales_person.allocated_amount = flt( + self.doc.net_total * sales_person.allocated_percentage / 100.0, + self.precision("allocated_amount", sales_person)) + + total += sales_person.allocated_percentage + + if sales_team and total != 100.0: + msgprint(_("Total") + " " + + _(self.meta.get_label("allocated_percentage", parentfield="sales_team")) + + " " + _("should be 100%"), raise_exception=True) + def get_current_tax_amount(self, item, tax, item_tax_map): tax_rate = self._get_tax_rate(tax, item_tax_map) + current_tax_amount = 0.0 if tax.charge_type == "Actual": # distribute the tax amount proportionally to each item row @@ -288,20 +374,18 @@ class SellingController(StockController): msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \ _("Please specify a valid") + " %(row_id_label)s") % { "idx": tax.idx, - "taxes_doctype": tax.parenttype, + "taxes_doctype": tax.doctype, "row_id_label": self.meta.get_label("row_id", parentfield="other_charges") }, raise_exception=True) def validate_inclusive_tax(self, tax): - def _on_previous_row_error(tax, row_range): - msgprint((_("Row") - + " # %(idx)s [%(taxes_doctype)s] [%(charge_type_label)s = \"%(charge_type)s\"]: " - + _("If:") + ' "%(inclusive_label)s" = ' + _("checked") + ", " - + _("then it is required that:") + " [" + _("Row") + " # %(row_range)s] " - + '"%(inclusive_label)s" = ' + _("checked")) % { + def _on_previous_row_error(row_range): + msgprint((_("Row") + " # %(idx)s [%(doctype)s]: " + + _("to be included in Item's rate, it is required that: ") + + " [" + _("Row") + " # %(row_range)s] " + _("also be included in Item's rate")) % { "idx": tax.idx, - "taxes_doctype": tax.doctype, + "doctype": tax.doctype, "inclusive_label": self.meta.get_label("included_in_print_rate", parentfield="other_charges"), "charge_type_label": self.meta.get_label("charge_type", @@ -312,12 +396,12 @@ class SellingController(StockController): if cint(tax.included_in_print_rate): if tax.charge_type == "Actual": - # inclusive cannot be of type Actual + # inclusive tax cannot be of type Actual msgprint((_("Row") - + " # %(idx)s [%(taxes_doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" " + + " # %(idx)s [%(doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" " + "cannot be included in Item's rate") % { "idx": tax.idx, - "taxes_doctype": tax.doctype, + "doctype": tax.doctype, "charge_type_label": self.meta.get_label("charge_type", parentfield="other_charges"), "charge_type": tax.charge_type, @@ -325,16 +409,14 @@ class SellingController(StockController): elif tax.charge_type == "On Previous Row Amount" and \ not cint(self.tax_doclist[tax.row_id - 1].included_in_print_rate): # referred row should also be inclusive - _on_previous_row_error(tax, tax.row_id) + _on_previous_row_error(tax.row_id) elif tax.charge_type == "On Previous Row Total" and \ - not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:tax.idx - 1]]): - # all rows about this tax should be inclusive - _on_previous_row_error(tax, "1 - %d" % (tax.idx - 1,)) + not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:tax.row_id - 1]]): + # all rows about the reffered tax should be inclusive + _on_previous_row_error("1 - %d" % (tax.row_id,)) def _load_item_tax_rate(self, item_tax_rate): - if not item_tax_rate: - return {} - return json.loads(item_tax_rate) + return json.loads(item_tax_rate) if item_tax_rate else {} def _get_tax_rate(self, tax, item_tax_map): if item_tax_map.has_key(tax.account_head): @@ -344,10 +426,9 @@ class SellingController(StockController): def _cleanup(self): for tax in self.tax_doclist: - del tax.fields["grand_total_for_current_item"] - del tax.fields["tax_amount_for_current_item"] - - for fieldname in ("tax_fraction_for_current_item", + for fieldname in ("grand_total_for_current_item", + "tax_amount_for_current_item", + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"): if fieldname in tax.fields: del tax.fields[fieldname] diff --git a/patches/may_2013/p01_selling_net_total_export.py b/patches/may_2013/p01_selling_net_total_export.py index dd0f68ac0a..7d4e3463b9 100644 --- a/patches/may_2013/p01_selling_net_total_export.py +++ b/patches/may_2013/p01_selling_net_total_export.py @@ -1,10 +1,29 @@ from __future__ import unicode_literals import webnotes +from webnotes.utils import cint def execute(): for module, doctype in (("Accounts", "Sales Invoice"), ("Selling", "Sales Order"), ("Selling", "Quotation"), ("Stock", "Delivery Note")): webnotes.reload_doc(module, "DocType", doctype) webnotes.conn.sql("""update `tab%s` - set net_total_export = round(net_total / if(conversion_rate=0, 1, ifnull(conversion_rate, 1)), 2)""" % - (doctype,)) \ No newline at end of file + set net_total_export = round(net_total / if(conversion_rate=0, 1, ifnull(conversion_rate, 1)), 2), + other_charges_total_export = round(grand_total_export - net_total_export, 2)""" % + (doctype,)) + + for module, doctype in (("Accounts", "Sales Invoice Item"), ("Selling", "Sales Order Item"), ("Selling", "Quotation Item"), + ("Stock", "Delivery Note Item")): + if cint(webnotes.conn.get_value("DocField", {"parent": doctype, "fieldname": "ref_rate"}, "read_only")) == 0 and \ + not webnotes.conn.sql("""select name from `tabProperty Setter` where doc_type=%s and doctype_or_field='DocField' + and field_name='ref_rate' and property='read_only'""", doctype): + webnotes.bean({ + "doctype": "Property Setter", + "doc_type": doctype, + "doctype_or_field": "DocField", + "field_name": "ref_rate", + "property": "read_only", + "property_type": "Check", + "value": "0" + }).insert() + + webnotes.reload_doc(module, "DocType", doctype) \ No newline at end of file diff --git a/selling/doctype/quotation/quotation.py b/selling/doctype/quotation/quotation.py index 7e83131204..f4c30fcb93 100644 --- a/selling/doctype/quotation/quotation.py +++ b/selling/doctype/quotation/quotation.py @@ -95,17 +95,6 @@ class DocType(SellingController): def get_rate(self,arg): return get_obj('Sales Common').get_rate(arg) - # Load Default Charges - # ---------------------------------------------------------- - def load_default_taxes(self): - self.doclist = get_obj('Sales Common').load_default_taxes(self) - - # Pull details from other charges master (Get Sales Taxes and Charges Master) - # ---------------------------------------------------------- - def get_other_charges(self): - self.doclist = get_obj('Sales Common').get_other_charges(self) - - # GET TERMS AND CONDITIONS # ==================================================================================== def get_tc_details(self): diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt index 24a080bac1..01b34ae1e0 100644 --- a/selling/doctype/quotation/quotation.txt +++ b/selling/doctype/quotation/quotation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-06 12:03:40", + "creation": "2013-05-21 16:16:40", "docstatus": 0, - "modified": "2013-05-06 13:07:37", + "modified": "2013-05-21 18:29:59", "modified_by": "Administrator", "owner": "Administrator" }, @@ -214,6 +214,7 @@ "oldfieldtype": "Table", "options": "Quotation Item", "read_only": 0, + "reqd": 1, "width": "40px" }, { @@ -431,13 +432,22 @@ "doctype": "DocField", "fieldname": "other_charges_total", "fieldtype": "Currency", - "label": "Taxes and Charges Total*", + "label": "Taxes and Charges Total", "oldfieldname": "other_charges_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1 }, + { + "doctype": "DocField", + "fieldname": "other_charges_total_export", + "fieldtype": "Currency", + "label": "Taxes and Charges Total (Export)", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "other_charges_calculation", diff --git a/selling/doctype/quotation_item/quotation_item.txt b/selling/doctype/quotation_item/quotation_item.txt index dccc503764..38d2fcf888 100644 --- a/selling/doctype/quotation_item/quotation_item.txt +++ b/selling/doctype/quotation_item/quotation_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:52", + "creation": "2013-04-19 13:30:50", "docstatus": 0, - "modified": "2013-03-07 07:03:29", + "modified": "2013-05-21 16:45:44", "modified_by": "Administrator", "owner": "Administrator" }, @@ -115,6 +115,7 @@ "options": "currency", "print_hide": 1, "print_width": "100px", + "read_only": 1, "reqd": 0, "width": "100px" }, @@ -188,6 +189,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "read_only": 1, "reqd": 0, "search_index": 0, "width": "100px" diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js index 67c7539b05..85e9fd7aab 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -25,28 +25,78 @@ wn.provide("erpnext.selling"); erpnext.selling.SellingController = wn.ui.form.Controller.extend({ setup: function() { - + this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate"); }, + // events when rendering form + // 1 + onload: function() { + var me = this; + this.toggle_rounded_total(); + if(this.frm.doc.__islocal) { + // set date fields + $.each(["posting_date", "due_date", "transaction_date"], function(i, fieldname) { + if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) { + me.frm.set_value(fieldname, get_today()); + } + }); + + // set currency fields + $.each(["currency", "price_list_currency"], function(i, fieldname) { + if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) { + me.frm.set_value(fieldname, wn.defaults.get_default("currency")); + } + }); + + // status + if(!this.frm.doc.status) this.frm.set_value("status", "Draft"); + + // TODO set depends_on for customer related fields + } + }, + + // 2 refresh: function() { + erpnext.hide_naming_series(); + this.toggle_price_list_fields(); + // TODO + // display item wise taxes in an html table + }, + + // 3 + onload_post_render: function() { + if(this.frm.doc.__islocal && this.frm.doc.company) { + var me = this; + this.frm.call({ + doc: this.frm.doc, + method: "onload_post_render", + freeze: true, + callback: function(r) { + // remove this call when using client side mapper + me.set_default_values(); + + me.frm.refresh(); + } + }); + } + }, + + validate: function() { + this.calculate_taxes_and_totals(); + + // TODO calc adjustment amount + }, + + barcode: function(doc, cdt, cdn) { + this.item_code(doc, cdt, cdn); }, item_code: function(doc, cdt, cdn) { var me = this; var item = wn.model.get_doc(cdt, cdn); - if(item.item_code) { - var fetch = true; - $.each(["company", "customer"], function(i, fieldname) { - if(!me.frm.doc[fieldname]) { - fetch = false; - msgprint(wn._("Please specify") + ": " + - wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + - ". " + wn._("It is needed to fetch Item Details.")); - } - }); - - if(!fetch) { + if(item.item_code || item.barcode) { + if(!this.validate_company_and_party()) { item.item_code = null; refresh_field("item_code", item.name, item.parentfield); } else { @@ -56,6 +106,7 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ args: { args: { item_code: item.item_code, + barcode: item.barcode, warehouse: item.warehouse, doctype: me.frm.doc.doctype, customer: me.frm.doc.customer, @@ -65,19 +116,186 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ price_list_currency: me.frm.doc.price_list_currency, plc_conversion_rate: me.frm.doc.plc_conversion_rate, company: me.frm.doc.company, - order_type: me.frm.doc.order_type - + order_type: me.frm.doc.order_type, + is_pos: cint(me.frm.doc.is_pos), + update_stock: cint(me.frm.doc.update_stock), } }, callback: function(r) { - // TODO: calculate + if(!r.exc) { + me.ref_rate(me.frm.doc, cdt, cdn); + } } }); } } }, - update_item_details: function() { + company: function() { + if(this.frm.doc.company) { + var me = this; + var company_currency = wn.model.get_doc(":Company", this.frm.doc.company).default_currency; + $.each(["currency", "price_list_currency"], function(i, fieldname) { + if(!me.doc[fieldname]) { + me.frm.set_value(fieldname, company_currency); + + // TODO - check this + me.frm.runclientscript(fieldname); + } + }); + } + }, + + customer: function() { + if(this.frm.doc.customer || this.frm.doc.debit_to) { + if(!this.frm.doc.company) { + this.frm.set_value("customer", null); + msgprint(wn._("Please specify Company")); + } else { + var me = this; + var price_list_name = this.frm.doc.price_list_name; + + this.frm.call({ + doc: this.frm.doc, + method: "set_customer_defaults", + freeze: true, + callback: function(r) { + if(!r.exc) { + me.frm.refresh(); + if(me.frm.doc.price_list_name !== price_list_name) me.price_list_name(); + } + } + }); + } + } + + // TODO hide/unhide related fields + }, + + // TODO + price_list_name: function() { + console.log("price_list_name"); + }, + + ref_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["ref_rate", "adj_rate"]); + + item.export_rate = flt(item.ref_rate * (1 - item.adj_rate / 100.0), + precision("export_rate", item)); + + this.calculate_taxes_and_totals(); + }, + + qty: function(doc, cdt, cdn) { + this.calculate_taxes_and_totals(); + }, + + adj_rate: function(doc, cdt, cdn) { + this.ref_rate(doc, cdt, cdn); + }, + + export_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["export_rate", "ref_rate"]); + + if(item.ref_rate) { + item.adj_rate = flt((1 - item.export_rate / item.ref_rate) * 100.0, + precision("adj_rate", item)); + } else { + item.adj_rate = 0.0; + } + + this.calculate_taxes_and_totals(); + }, + + included_in_print_rate: function(doc, cdt, cdn) { + var tax = wn.model.get_doc(cdt, cdn); + try { + this.validate_on_previous_row(tax); + this.validate_inclusive_tax(tax); + } catch(e) { + tax.included_in_print_rate = 0; + refresh_field("included_in_print_rate", tax.name, tax.parentfield); + throw e; + } + }, + + commission_rate: function() { + this.calculate_commission(); + refresh_field("total_commission"); + }, + + total_commission: function() { + if(this.frm.doc.net_total) { + wn.model.round_floats_in(this.frm.doc, ["net_total", "total_commission"]); + + if(this.frm.doc.net_total < this.frm.doc.total_commission) { + var msg = (wn._("[Error]") + " " + + wn._(wn.meta.get_label(this.frm.doc.doctype, "total_commission", + this.frm.doc.name)) + " > " + + wn._(wn.meta.get_label(this.frm.doc.doctype, "net_total", this.frm.doc.name))); + msgprint(msg); + throw msg; + } + + this.frm.set_value("commission_rate", + flt(this.frm.doc.total_commission * 100.0 / this.frm.doc.net_total)); + } + }, + + allocated_percentage: function(doc, cdt, cdn) { + var sales_person = wn.model.get_doc(cdt, cdn); + + if(sales_person.allocated_percentage) { + sales_person.allocated_percentage = flt(sales_person.allocated_percentage, + precision("allocated_percentage", sales_person)); + sales_person.allocated_amount = flt(this.frm.doc.net_total * + sales_person.allocated_percentage / 100.0, + precision("allocated_amount", sales_person)); + + refresh_field(["allocated_percentage", "allocated_amount"], sales_person.name, + sales_person.parentfield); + } + }, + + toggle_rounded_total: function() { + var me = this; + if(cint(wn.defaults.get_global_default("disable_rounded_total"))) { + $.each(["rounded_total", "rounded_total_export"], function(i, fieldname) { + me.frm.set_df_property(fieldname, "print_hide", 1); + me.frm.toggle_display(fieldname, false); + }); + } + }, + + validate_company_and_party: function() { + var me = this; + var valid = true; + $.each(["company", "customer"], function(i, fieldname) { + if(!me.frm.doc[fieldname]) { + valid = false; + msgprint(wn._("Please specify") + ": " + + wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + ". " + wn._("It is needed to fetch Item Details.")); + } + }); + return valid; + }, + + set_default_values: function() { + $.each(wn.model.get_doclist(this.frm.doctype, this.frm.docname), function(i, doc) { + var updated = wn.model.set_default_values(doc); + if(doc.parentfield) { + refresh_field(doc.parentfield); + } else { + refresh_field(updated); + } + }); + }, + + // TODO + toggle_price_list_fields: function() { }, @@ -85,7 +303,359 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ }, + calculate_taxes_and_totals: function() { + this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); + + // TODO validate conversion rate + + this.frm.item_doclist = this.get_item_doclist(); + this.frm.tax_doclist = this.get_tax_doclist(); + + this.calculate_item_values(); + this.initialize_taxes(); + this.determine_exclusive_rate(); + this.calculate_net_total(); + this.calculate_taxes(); + this.calculate_totals(); + this.calculate_commission(); + this.calculate_contribution(); + this._cleanup(); + + this.frm.doc.in_words = this.frm.doc.in_words_export = ""; + + // TODO + // outstanding amount + + // check for custom_recalc in custom scripts of server + + this.frm.refresh(); + + }, + get_item_doclist: function() { + return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: this.fname}); + }, + + get_tax_doclist: function() { + return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: "other_charges"}); + }, + + calculate_item_values: function() { + var me = this; + + var _set_base = function(item, print_field, base_field) { + // set values in base currency + item[base_field] = flt(item[print_field] * me.frm.doc.conversion_rate, + precision(base_field, item)); + }; + + $.each(this.frm.item_doclist, function(i, item) { + wn.model.round_floats_in(item); + item.export_amount = flt(item.export_rate * item.qty, precision("export_amount", item)); + + _set_base(item, "ref_rate", "base_ref_rate"); + _set_base(item, "export_rate", "basic_rate"); + _set_base(item, "export_amount", "amount"); + }); + + }, + + initialize_taxes: function() { + var me = this; + $.each(this.frm.tax_doclist, function(i, tax) { + tax.tax_amount = tax.total = 0.0; + tax.item_wise_tax_detail = {}; + + // temporary fields + tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0; + + me.validate_on_previous_row(tax); + me.validate_inclusive_tax(tax); + + wn.model.round_floats_in(tax); + }); + }, + + determine_exclusive_rate: function() { + var me = this; + $.each(me.frm.item_doclist, function(n, item) { + var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); + var cumulated_tax_fraction = 0.0; + + $.each(me.frm.tax_doclist, function(i, tax) { + tax.tax_fraction_for_current_item = me.get_current_tax_fraction(tax, item_tax_map); + + if(i==0) { + tax.grand_total_for_current_item = 1 + tax.tax_fraction_for_current_item; + } else { + tax.grand_total_for_current_item = + me.frm.tax_doclist[i-1].grand_total_for_current_item + + tax.tax_fraction_for_current_item; + } + + cumulated_tax_fraction += tax.tax_fraction_for_current_item; + }); + + if(cumulated_tax_fraction) { + item.basic_rate = flt( + (item.export_rate * me.frm.doc.conversion_rate) / (1 + cumulated_tax_fraction), + precision("basic_rate", item)); + + item.amount = flt(item.basic_rate * item.qty, precision("amount", item)); + + if(item.adj_rate == 100) { + item.base_ref_rate = item.basic_rate; + item.basic_rate = 0.0; + } else { + item.base_ref_rate = flt(item.basic_rate / (1 - item.adj_rate / 100.0), + precision("base_ref_rate", item)); + } + } + }); + }, + + get_current_tax_fraction: function(tax, item_tax_map) { + // Get tax fraction for calculating tax exclusive amount + // from tax inclusive amount + var current_tax_fraction = 0.0; + + if(cint(tax.included_in_print_rate)) { + var tax_rate = me._get_tax_rate(tax, item_tax_map); + + if(tax.charge_type == "On Net Total") { + current_tax_fraction = (tax_rate / 100.0); + + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_fraction = (tax_rate / 100.0) * + me.frm.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item; + + } else if(tax.charge_type == "On Previous Row Total") { + current_tax_fraction = (tax_rate / 100.0) * + me.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item; + } + } + + return current_tax_fraction; + }, + + calculate_net_total: function() { + var me = this; + + this.frm.doc.net_total = this.frm.doc.net_total_export = 0.0; + $.each(this.frm.item_doclist, function(i, item) { + me.frm.doc.net_total += item.amount; + me.frm.doc.net_total_export += item.export_amount; + }); + + wn.model.round_floats_in(this.frm.doc, ["net_total", "net_total_export"]); + }, + + calculate_taxes: function() { + var me = this; + + $.each(this.frm.item_doclist, function(n, item) { + var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); + + $.each(me.frm.tax_doclist, function(i, tax) { + // tax_amount represents the amount of tax for the current step + var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map); + + // case when net total is 0 but there is an actual type charge + // in this case add the actual amount to tax.tax_amount + // and tax.grand_total_for_current_item for the first such iteration + if(tax.charge_type == "Actual" && + !(current_tax_amount || me.frm.doc.net_total || tax.tax_amount)) { + var zero_net_total_adjustment = flt(tax.rate, precision("tax_amount", tax)); + current_tax_amount += zero_net_total_adjustment; + } + + // store tax_amount for current item as it will be used for + // charge type = 'On Previous Row Amount' + tax.tax_amount_for_current_item = current_tax_amount; + + // accumulate tax amount into tax.tax_amount + tax.tax_amount += current_tax_amount; + + // Calculate tax.total viz. grand total till that step + // note: grand_total_for_current_item contains the contribution of + // item's amount, previously applied tax and the current tax on that item + if(i==0) { + tax.grand_total_for_current_item = flt(item.amount + current_tax_amount, + precision("total", tax)); + } else { + tax.grand_total_for_current_item = + flt(me.frm.tax_doclist[i-1].grand_total_for_current_item + current_tax_amount, + precision("total", tax)); + } + + // in tax.total, accumulate grand total for each item + tax.total += tax.grand_total_for_current_item; + + // store tax breakup for each item + tax.item_wise_tax_detail[item.item_code || item.item_name] = current_tax_amount; + + }); + }); + }, + + get_current_tax_amount: function(item, tax, item_tax_map) { + var tax_rate = this._get_tax_rate(tax, item_tax_map); + var current_tax_amount = 0.0; + + if(tax.charge_type == "Actual") { + // distribute the tax amount proportionally to each item row + var actual = flt(tax.rate, precision("tax_amount", tax)); + current_tax_amount = this.frm.doc.net_total ? + ((item.amount / this.frm.doc.net_total) * actual) : + 0.0; + + } else if(tax.charge_type == "On Net Total") { + current_tax_amount = (tax_rate / 100.0); + + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_amount = (tax_rate / 100.0) * + me.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item; + + } else if(tax.charge_type == "On Previous Row Total") { + current_tax_amount = (tax_rate / 100.0) * + me.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; + + } + + return flt(current_tax_amount, precision("tax_amount", tax)); + }, + + calculate_totals: function() { + var tax_count = this.frm.tax_doclist.length; + this.frm.doc.grand_total = flt( + tax_count ? this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total, + precision("grand_total")); + this.frm.doc.grand_total_export = flt(this.frm.doc.grand_total / this.frm.doc.conversion_rate, + precision("grand_total_export")); + + this.frm.doc.other_charges_total = flt(this.frm.doc.grand_total - this.frm.doc.net_total, + precision("other_charges_total")); + this.frm.doc.other_charges_total_export = flt( + this.frm.doc.grand_total_export - this.frm.doc.net_total_export, + precision("other_charges_total_export")); + + this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total); + this.frm.doc.rounded_total_export = Math.round(this.frm.doc.grand_total_export); + }, + + calculate_commission: function() { + if(this.frm.doc.commission_rate > 100) { + var msg = wn._(wn.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) + + " " + wn._("cannot be greater than 100"); + msgprint(msg); + throw msg; + } + + this.frm.doc.total_commission = flt(this.frm.doc.net_total * this.frm.doc.commission_rate / 100.0, + precision("total_commission")); + }, + + calculate_contribution: function() { + $.each(wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: "sales_team"}), function(i, sales_person) { + wn.model.round_floats_in(sales_person); + if(sales_person.allocated_percentage) { + sales_person.allocated_amount = flt( + me.frm.doc.net_total * sales_person.allocated_percentage / 100.0, + precision("allocated_amount", sales_person)); + } + }); + }, + + _cleanup: function() { + $.each(this.frm.tax_doclist, function(i, tax) { + var tax_fields = keys(tax); + $.each(["tax_amount_for_current_item", "grand_total_for_current_item", + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"], + function(i, fieldname) { delete tax[fieldname];}); + + tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail); + }); + }, + + validate_on_previous_row: function(tax) { + // validate if a valid row id is mentioned in case of + // On Previous Row Amount and On Previous Row Total + if((["On Previous Row Amount", "On Previous Row Total"].indexOf(tax.charge_type) != -1) && + (!tax.row_id || cint(tax.row_id) >= tax.idx)) { + var msg = repl(wn._("Row") + " # %(idx)s [%(doctype)s]: " + + wn._("Please specify a valid") + " %(row_id_label)s", { + idx: tax.idx, + doctype: tax.doctype, + row_id_label: wn.meta.get_label(tax.doctype, "row_id", tax.name) + }); + msgprint(msg); + throw msg; + } + }, + + validate_inclusive_tax: function(tax) { + if(!this.frm.tax_doclist) this.frm.tax_doclist = this.get_tax_doclist(); + + var actual_type_error = function() { + var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " + + "%(charge_type_label)s = \"%(charge_type)s\" " + + wn._("cannot be included in Item's rate"), { + idx: tax.idx, + doctype: tax.doctype, + charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name), + charge_type: tax.charge_type + }); + msgprint(msg); + throw msg; + }; + + var on_previous_row_error = function(row_range) { + var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " + + wn._("to be included in Item's rate, it is required that: ") + + " [" + wn._("Row") + " # %(row_range)s] " + wn._("also be included in Item's rate"), { + idx: tax.idx, + doctype: tax.doctype, + charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name), + charge_type: tax.charge_type, + inclusive_label: wn.meta.get_label(tax.doctype, "included_in_print_rate", tax.name), + row_range: row_range, + }); + + msgprint(msg); + throw msg; + }; + + if(cint(tax.included_in_print_rate)) { + if(tax.charge_type == "Actual") { + // inclusive tax cannot be of type Actual + actual_type_error(); + } else if(tax.charge_type == "On Previous Row Amount" && + !cint(this.frm.tax_doclist[tax.row_id - 1].included_in_print_rate)) { + // referred row should also be an inclusive tax + on_previous_row_error(tax.row_id); + } else if(tax.charge_type == "On Previous Row Total") { + var taxes_not_included = $.map(this.frm.tax_doclist.slice(0, tax.row_id), + function(t) { return cint(t.included_in_print_rate) ? null : t; }); + if(taxes_not_included.length > 0) { + // all rows above this tax should be inclusive + on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id); + } + } + } + }, + + _load_item_tax_rate: function(item_tax_rate) { + return item_tax_rate ? JSON.parse(item_tax_rate) : {}; + }, + + _get_tax_rate: function(tax, item_tax_map) { + return (keys(item_tax_map).indexOf(tax.account_head) != -1) ? + flt(item_tax_map.get(tax.account_head), precision("rate", tax)) : + tax.rate; + }, }); // to save previous state of cur_frm.cscript @@ -98,65 +668,6 @@ cur_frm.cscript = new erpnext.selling.SellingController({frm: cur_frm}); $.extend(cur_frm.cscript, prev_cscript); -// ============== Load Default Taxes =================== -cur_frm.cscript.load_taxes = function(doc, cdt, cdn, callback) { - // run if this is not executed from dt_map... - doc = locals[doc.doctype][doc.name]; - if(doc.customer || getchildren('Sales Taxes and Charges', doc.name, 'other_charges', doc.doctype).length) { - if(callback) { - callback(doc, cdt, cdn); - } - } else if(doc.charge) { - cur_frm.cscript.get_charges(doc, cdt, cdn, callback); - } else { - $c_obj(make_doclist(doc.doctype, doc.name),'load_default_taxes','',function(r,rt){ - refresh_field('other_charges'); - if(callback) callback(doc, cdt, cdn); - }); - } -} - - -// Gets called after existing item details are update to fill in -// remaining default values -cur_frm.cscript.load_defaults = function(doc, dt, dn, callback) { - if(!cur_frm.doc.__islocal) { return; } - - doc = locals[doc.doctype][doc.name]; - var fields_to_refresh = wn.model.set_default_values(doc); - if(fields_to_refresh) { refresh_many(fields_to_refresh); } - - fields_to_refresh = null; - var children = getchildren(cur_frm.cscript.tname, doc.name, cur_frm.cscript.fname); - if(!children) { return; } - for(var i=0; i \ - Please either
\ - * Specify Basic Rate (i.e. Rate which will be displayed in print)
\ - -- or --
\ - * Uncheck 'Is this Tax included in Basic Rate?' in the tax entries of Taxes section."); - } - } -} - -// ************************ EXPORT RATE ************************* -cur_frm.cscript.export_rate = function(doc,cdt,cdn) { - var cur_rec = locals[cdt][cdn]; - var fname = cur_frm.cscript.fname; - var tname = cur_frm.cscript.tname; - if(flt(cur_rec.ref_rate)>0 && flt(cur_rec.export_rate)>0) { - var adj_rate = 100 * (1 - (flt(cur_rec.export_rate) / flt(cur_rec.ref_rate))); - set_multiple(tname, cur_rec.name, { 'adj_rate': adj_rate }, fname); - } - doc = locals[doc.doctype][doc.name]; - cur_frm.cscript.recalc(doc, 1); -} - - - // ************* GET OTHER CHARGES BASED ON COMPANY ************* cur_frm.fields_dict.charge.get_query = function(doc) { return 'SELECT DISTINCT `tabSales Taxes and Charges Master`.name FROM \ @@ -491,482 +906,4 @@ cur_frm.cscript.get_charges = function(doc, cdt, cdn, callback) { } -// CALCULATION OF TOTAL AMOUNTS -// ======================================================================================================== -cur_frm.cscript.recalc = function(doc, n) { - if(!n)n=0; - doc = locals[doc.doctype][doc.name]; - var tname = cur_frm.cscript.tname; - var fname = cur_frm.cscript.fname; - var sales_team = cur_frm.cscript.sales_team_fname; - var other_fname = cur_frm.cscript.other_fname; - - if(!flt(doc.conversion_rate)) { - doc.conversion_rate = 1; - refresh_field('conversion_rate'); - } - if(!flt(doc.plc_conversion_rate)) { - doc.plc_conversion_rate = 1; - refresh_field('plc_conversion_rate'); - } - - if(n > 0) cur_frm.cscript.update_fname_table(doc , tname , fname , n, other_fname); // updates all values in table (i.e. amount, export amount, net total etc.) - - if(flt(doc.net_total) > 0) { - var cl = getchildren('Sales Taxes and Charges', doc.name, other_fname,doc.doctype); - for(var i = 0; i1) { - net_total_incl *= flt(doc.conversion_rate); - } - - // TODO: store net_total_export - - doc.net_total = inclusive_rate ? flt(net_total_incl) : flt(net_total); - doc.other_charges_total = roundNumber(flt(other_charges_total), 2); - doc.grand_total = roundNumber((flt(net_total) + flt(other_charges_total)), 2); - doc.rounded_total = Math.round(doc.grand_total); - doc.grand_total_export = roundNumber((flt(doc.grand_total) / flt(doc.conversion_rate)), 2); - doc.rounded_total_export = Math.round(doc.grand_total_export); - doc.total_commission = flt(flt(net_total) * flt(doc.commission_rate) / 100); -} - -// ******************************* OTHER CHARGES ************************************* -cur_frm.cscript.calc_other_charges = function(doc , tname , fname , other_fname) { - doc = locals[doc.doctype][doc.name]; - - // Make Display Area - cur_frm.fields_dict['other_charges_calculation'].disp_area.innerHTML = - 'Calculation Details for Taxes and Charges:'; - - var cl = getchildren(tname, doc.name, fname); - var tax = getchildren('Sales Taxes and Charges', doc.name, other_fname,doc.doctype); - - // Make display table - var otc = make_table(cur_frm.fields_dict['other_charges_calculation'].disp_area, - cl.length + 1, tax.length + 1, '90%', [], { border:'1px solid #AAA', padding:'2px' }); - $y(otc,{marginTop:'8px'}); - - var tax_desc = {}; var tax_desc_rates = []; var net_total = 0; - - for(var i=0;i2) alert("You cannot enter more than 2 nos. for division"); - var id1 = cint(row[0].replace(/^\s+|\s+$/g,"")); - var id2 = cint(row[1].replace(/^\s+|\s+$/g,"")); - tax_amount = flt(tax[id1-1].total_amount) / flt(tax[id2-1].total_amount); - } - return tax_amount - } - else if(tax[t].charge_type == 'On Previous Row Total') { - if(flt(print_amt) == 1) { - doc.sales_tax_rate += flt(rate); - refresh_field('sales_tax_rate'); - return - } - var row = cint(tax[t].row_id); - return tax_amount = flt(rate) * (flt(tax[row-1].total_tax_amount)+flt(tax[row-1].total_amount)) / 100; - } -} - -// ********************** Functions for inclusive value calc ****************************** -cur_frm.cscript.consider_incl_rate = function(doc, other_fname) { - var tax_list = getchildren('Sales Taxes and Charges', doc.name, other_fname, doc.doctype); - for(var i=0; i \ - * On Net Total
\ - * On Previous Row Amount
\ - * On Previous Row Total"); - tax.included_in_print_rate = 0; - refresh_field('included_in_print_rate', tax.name, cur_frm.cscript.other_fname); - } - var tax_list = getchildren('Sales Taxes and Charges', doc.name, cur_frm.cscript.other_fname, doc.doctype); - cur_frm.cscript.validate_print_rate_option(doc, tax_list, tax.idx-1); - } -} - -// ********************** Update values in table ****************************** -cur_frm.cscript.update_fname_table = function(doc , tname , fname , n, other_fname) { - doc = locals[doc.doctype][doc.name] - var net_total = 0 - var cl = getchildren(tname, doc.name, fname); - var consider_incl_rate = cur_frm.cscript.consider_incl_rate(doc, other_fname); - for(var i=0;i 0) { - set_multiple(tname, cl[i].name, { - 'export_rate': flt(flt(cl[i].ref_rate) * (100 - flt(cl[i].adj_rate)) / 100) - }, fname); - } - set_multiple(tname, cl[i].name, { - 'export_amount': flt(flt(cl[i].qty) * flt(cl[i].export_rate)), - 'basic_rate': flt(flt(cl[i].export_rate) * flt(doc.conversion_rate)), - 'amount': roundNumber(flt((flt(cl[i].export_rate) * flt(doc.conversion_rate)) * flt(cl[i].qty)), 2) - }, fname); - //var base_ref_rate = flt(cl[i].basic_rate) + flt(flt(cl[i].basic_rate) * flt(cl[i].adj_rate) / 100); - //set_multiple(tname, cl[i].name, { - // 'base_ref_rate': flt(base_ref_rate) - //}, fname); - - } else if(consider_incl_rate) { - if(flt(cl[i].export_rate) > 0) { - // calculate basic rate based on taxes - // then calculate and set basic_rate, base_ref_rate, ref_rate, amount, export_amount - var ref_rate = flt(cl[i].adj_rate)!=flt(100) ? - flt((100 * flt(cl[i].export_rate))/flt(100 - flt(cl[i].adj_rate))) : - flt(0) - set_multiple(tname, cl[i].name, { 'ref_rate': ref_rate }, fname); - } else if((flt(cl[i].ref_rate) > 0) && (flt(cl[i].adj_rate) > 0)) { - var export_rate = flt(cl[i].ref_rate) * flt(1 - flt(cl[i].adj_rate / 100)); - set_multiple(tname, cl[i].name, { 'export_rate': flt(export_rate) }, fname); - } - //console.log("export_rate: " + cl[i].export_rate); - - var basic_rate = cur_frm.cscript.back_calc_basic_rate(doc, tname, fname, cl[i], other_fname); - var base_ref_rate = basic_rate + flt(basic_rate * flt(cl[i].adj_rate) / 100); - set_multiple(tname, cl[i].name, { - 'basic_rate': flt(basic_rate), - 'amount': roundNumber(flt(basic_rate * flt(cl[i].qty)), 2), - 'export_amount': flt(flt(cl[i].qty) * flt(cl[i].export_rate)), - 'base_ref_rate': flt(base_ref_rate) - }, fname); - } - } - else if(n == 2){ - if(flt(cl[i].ref_rate) > 0) - set_multiple(tname, cl[i].name, {'adj_rate': 100 - flt(flt(cl[i].basic_rate) * 100 / (flt(cl[i].ref_rate) * flt(doc.conversion_rate)))}, fname); - set_multiple(tname, cl[i].name, {'amount': flt(flt(cl[i].qty) * flt(cl[i].basic_rate)), 'export_rate': flt(flt(cl[i].basic_rate) / flt(doc.conversion_rate)), 'export_amount': flt((flt(cl[i].basic_rate) / flt(doc.conversion_rate)) * flt(cl[i].qty)) }, fname); - } - /*else if(n == 3){ - set_multiple(tname, cl[i].name, {'basic_rate': flt(flt(cl[i].export_rate) * flt(doc.conversion_rate))}, fname); - set_multiple(tname, cl[i].name, {'amount' : flt(flt(cl[i].basic_rate) * flt(cl[i].qty)), 'export_amount': flt(flt(cl[i].export_rate) * flt(cl[i].qty))}, fname); - if(cl[i].ref_rate > 0) - set_multiple(tname, cl[i].name, {'adj_rate': 100 - flt(flt(cl[i].export_rate) * 100 / flt(cl[i].ref_rate)), 'base_ref_rate': flt(flt(cl[i].ref_rate) * flt(doc.conversion_rate)) }, fname); - }*/ - net_total += flt(flt(cl[i].qty) * flt(cl[i].basic_rate)); - } - doc.net_total = net_total; - refresh_field('net_total'); -} - -cur_frm.cscript.get_item_wise_tax_detail = function( doc, rate, cl, i, tax, t) { - doc = locals[doc.doctype][doc.name]; - var detail = ''; - detail = cl[i].item_code + " : " + cstr(rate) + NEWLINE; - return detail; -} - -// **************** RE-CALCULATE VALUES *************************** - -cur_frm.cscript.recalculate_values = function(doc, cdt, cdn) { - cur_frm.cscript.calculate_charges(doc,cdt,cdn); -} - -cur_frm.cscript.validate_print_rate_option = function(doc, taxes, i) { - if(in_list(['On Previous Row Amount','On Previous Row Total'], taxes[i].charge_type)) { - if(!taxes[i].row_id){ - alert("Please Enter Row on which amount needs to be calculated for row : "+taxes[i].idx); - validated = false; - } else if(taxes[i].included_in_print_rate && taxes[taxes[i].row_id-1].charge_type=='Actual') { - msgprint("Row of type 'Actual' cannot be depended on for type '" + taxes[i].charge_type + "'\ - when using tax inclusive prices.
\ - This will lead to incorrect values.

\ - Please specify correct value in 'Enter Row' column of Row: " - + taxes[i].idx + " in Taxes table"); - validated = false; - taxes[i].included_in_print_rate = 0; - refresh_field('included_in_print_rate', taxes[i].name, other_fname); - } else if ((taxes[i].included_in_print_rate && !taxes[taxes[i].row_id-1].included_in_print_rate) || - (!taxes[i].included_in_print_rate && taxes[taxes[i].row_id-1].included_in_print_rate)) { - msgprint("If any row in the tax table depends on 'Previous Row Amount/Total',
\ - 'Is this Tax included in Basic Rate?' column should be same for both row
\ - i.e for that row and the previous row.

\ - The same is violated for row #"+(i+1)+" and row #"+taxes[i].row_id - ); - validated = false; - } - } -} - -cur_frm.cscript.calculate_charges = function(doc, cdt, cdn) { - var other_fname = cur_frm.cscript.other_fname; - - var cl = getchildren('Sales Taxes and Charges', doc.name, other_fname, doc.doctype); - for(var i = 0; i 100){ - alert("Commision rate cannot be greater than 100."); - doc.total_commission = 0; - doc.commission_rate = 0; - } else { - doc.total_commission = doc.net_total * doc.commission_rate / 100; - } - refresh_many(['total_commission','commission_rate']); - -} - -// *******Total Commission Trigger (calculates commission rate)********* -cur_frm.cscript.total_commission = function(doc, cdt, cdn) { - if(doc.net_total){ - if(doc.net_total < doc.total_commission){ - alert("Total commission cannot be greater than net total."); - doc.total_commission = 0; - doc.commission_rate = 0; - } else { - doc.commission_rate = doc.total_commission * 100 / doc.net_total; - } - refresh_many(['total_commission','commission_rate']); - } -} -// Sales Person Allocated % trigger -// ============================================================================== -cur_frm.cscript.allocated_percentage = function(doc, cdt, cdn) { - var fname = cur_frm.cscript.sales_team_fname; - var d = locals[cdt][cdn]; - if (d.allocated_percentage) { - d.allocated_amount = flt(flt(doc.net_total)*flt(d.allocated_percentage)/100); - refresh_field('allocated_amount', d.name, fname); - } -} - -// Client Side Validation -// ================================================================================= -cur_frm.cscript.validate = function(doc, cdt, cdn) { - cur_frm.cscript.validate_items(doc); - var cl = getchildren('Sales Taxes and Charges Master', doc.name, 'other_charges'); - for(var i =0;i now() - or end_of_life = '0000-00-00') and (is_sales_item = 'Yes' - or is_service_item = 'Yes')""", args['item_code'], as_dict=1) - tax = webnotes.conn.sql("""select tax_type, tax_rate from `tabItem Tax` - where parent = %s""", args['item_code']) - t = {} - for x in tax: t[x[0]] = flt(x[1]) - ret = { - 'description': item and item[0]['description_html'] or \ - item[0]['description'], - 'barcode': item and item[0]['barcode'] or '', - 'item_group': item and item[0]['item_group'] or '', - 'item_name': item and item[0]['item_name'] or '', - 'brand': item and item[0]['brand'] or '', - 'stock_uom': item and item[0]['stock_uom'] or '', - 'reserved_warehouse': item and item[0]['default_warehouse'] or '', - 'warehouse': item and item[0]['default_warehouse'] or \ - args.get('warehouse'), - 'income_account': item and item[0]['default_income_account'] or \ - args.get('income_account'), - 'expense_account': item and item[0]['purchase_account'] or \ - args.get('expense_account'), - 'cost_center': item and item[0]['default_sales_cost_center'] or \ - args.get('cost_center'), - # this is done coz if item once fetched is fetched again than its qty shld be reset to 1 - 'qty': 1.00, - 'adj_rate': 0, - 'amount': 0, - 'export_amount': 0, - 'item_tax_rate': json.dumps(t), - 'batch_no': '' - } - if(obj.doc.price_list_name and item): #this is done to fetch the changed BASIC RATE and REF RATE based on PRICE LIST - base_ref_rate = self.get_ref_rate(args['item_code'], obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate) - ret['ref_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - ret['export_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - ret['base_ref_rate'] = flt(base_ref_rate) - ret['basic_rate'] = flt(base_ref_rate) - - if ret['warehouse'] or ret['reserved_warehouse']: - av_qty = self.get_available_qty({'item_code': args['item_code'], 'warehouse': ret['warehouse'] or ret['reserved_warehouse']}) - ret.update(av_qty) - - # get customer code for given item from Item Customer Detail - customer_item_code_row = webnotes.conn.sql("""\ - select ref_code from `tabItem Customer Detail` - where parent = %s and customer_name = %s""", - (args['item_code'], obj.doc.customer)) - if customer_item_code_row and customer_item_code_row[0][0]: - ret['customer_item_code'] = customer_item_code_row[0][0] - - return ret - + # def get_item_details(self, args, obj): + # import json + # if not obj.doc.price_list_name: + # msgprint("Please Select Price List before selecting Items", raise_exception=True) + # item = webnotes.conn.sql("""select description, item_name, brand, item_group, stock_uom, + # default_warehouse, default_income_account, default_sales_cost_center, + # purchase_account, description_html, barcode from `tabItem` + # where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now() + # or end_of_life = '0000-00-00') and (is_sales_item = 'Yes' + # or is_service_item = 'Yes')""", args['item_code'], as_dict=1) + # tax = webnotes.conn.sql("""select tax_type, tax_rate from `tabItem Tax` + # where parent = %s""", args['item_code']) + # t = {} + # for x in tax: t[x[0]] = flt(x[1]) + # ret = { + # 'description': item and item[0]['description_html'] or \ + # item[0]['description'], + # 'barcode': item and item[0]['barcode'] or '', + # 'item_group': item and item[0]['item_group'] or '', + # 'item_name': item and item[0]['item_name'] or '', + # 'brand': item and item[0]['brand'] or '', + # 'stock_uom': item and item[0]['stock_uom'] or '', + # 'reserved_warehouse': item and item[0]['default_warehouse'] or '', + # 'warehouse': item and item[0]['default_warehouse'] or \ + # args.get('warehouse'), + # 'income_account': item and item[0]['default_income_account'] or \ + # args.get('income_account'), + # 'expense_account': item and item[0]['purchase_account'] or \ + # args.get('expense_account'), + # 'cost_center': item and item[0]['default_sales_cost_center'] or \ + # args.get('cost_center'), + # # this is done coz if item once fetched is fetched again than its qty shld be reset to 1 + # 'qty': 1.00, + # 'adj_rate': 0, + # 'amount': 0, + # 'export_amount': 0, + # 'item_tax_rate': json.dumps(t), + # 'batch_no': '' + # } + # if(obj.doc.price_list_name and item): #this is done to fetch the changed BASIC RATE and REF RATE based on PRICE LIST + # base_ref_rate = self.get_ref_rate(args['item_code'], obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate) + # ret['ref_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) + # ret['export_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) + # ret['base_ref_rate'] = flt(base_ref_rate) + # ret['basic_rate'] = flt(base_ref_rate) + # + # if ret['warehouse'] or ret['reserved_warehouse']: + # av_qty = self.get_available_qty({'item_code': args['item_code'], 'warehouse': ret['warehouse'] or ret['reserved_warehouse']}) + # ret.update(av_qty) + # + # # get customer code for given item from Item Customer Detail + # customer_item_code_row = webnotes.conn.sql("""\ + # select ref_code from `tabItem Customer Detail` + # where parent = %s and customer_name = %s""", + # (args['item_code'], obj.doc.customer)) + # if customer_item_code_row and customer_item_code_row[0][0]: + # ret['customer_item_code'] = customer_item_code_row[0][0] + # + # return ret + # TODO: deprecate it def get_item_defaults(self, args): item = webnotes.conn.sql("""select default_warehouse, default_income_account, default_sales_cost_center, purchase_account from `tabItem` where name = %s @@ -200,97 +183,78 @@ class DocType(TransactionBase): return ret - def get_available_qty(self,args): - tot_avail_qty = webnotes.conn.sql("select projected_qty, actual_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (args['item_code'], args['warehouse']), as_dict=1) - ret = { - 'projected_qty' : tot_avail_qty and flt(tot_avail_qty[0]['projected_qty']) or 0, - 'actual_qty' : tot_avail_qty and flt(tot_avail_qty[0]['actual_qty']) or 0 - } - return ret + # def get_available_qty(self,args): +# tot_avail_qty = webnotes.conn.sql("select projected_qty, actual_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (args['item_code'], args['warehouse']), as_dict=1) +# ret = { +# 'projected_qty' : tot_avail_qty and flt(tot_avail_qty[0]['projected_qty']) or 0, +# 'actual_qty' : tot_avail_qty and flt(tot_avail_qty[0]['actual_qty']) or 0 +# } +# return ret # ***************** Get Ref rate as entered in Item Master ******************** - def get_ref_rate(self, item_code, price_list_name, price_list_currency, plc_conv_rate): - ref_rate = webnotes.conn.sql("select ref_rate from `tabItem Price` where parent = %s and price_list_name = %s and ref_currency = %s and selling=1", - (item_code, price_list_name, price_list_currency)) - base_ref_rate = ref_rate and flt(ref_rate[0][0]) * flt(plc_conv_rate) or 0 - return base_ref_rate + # def get_ref_rate(self, item_code, price_list_name, price_list_currency, plc_conv_rate): + # ref_rate = webnotes.conn.sql("select ref_rate from `tabItem Price` where parent = %s and price_list_name = %s and ref_currency = %s and selling=1", + # (item_code, price_list_name, price_list_currency)) + # base_ref_rate = ref_rate and flt(ref_rate[0][0]) * flt(plc_conv_rate) or 0 + # return base_ref_rate - def get_barcode_details(self, barcode): - item = webnotes.conn.sql("select name, end_of_life, is_sales_item, is_service_item \ - from `tabItem` where barcode = %s", barcode, as_dict=1) - ret = {} - if not item: - msgprint("""No item found for this barcode: %s. - May be barcode not updated in item master. Please check""" % barcode) - elif item[0]['end_of_life'] and getdate(cstr(item[0]['end_of_life'])) < nowdate(): - msgprint("Item: %s has been expired. Please check 'End of Life' field in item master" % item[0]['name']) - elif item[0]['is_sales_item'] == 'No' and item[0]['is_service_item'] == 'No': - msgprint("Item: %s is not a sales or service item" % item[0]['name']) - elif len(item) > 1: - msgprint("There are multiple item for this barcode. \nPlease select item code manually") - else: - ret = {'item_code': item and item[0]['name'] or ''} - - return ret - - # ****** Re-cancellculates Basic Rate & amount based on Price List Selected ****** - def get_adj_percent(self, obj): - for d in getlist(obj.doclist, obj.fname): - base_ref_rate = self.get_ref_rate(d.item_code, obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate) - d.adj_rate = 0 - d.ref_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - d.basic_rate = flt(base_ref_rate) - d.base_ref_rate = flt(base_ref_rate) - d.export_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - d.amount = flt(d.qty)*flt(base_ref_rate) - d.export_amount = flt(d.qty)*flt(base_ref_rate)/flt(obj.doc.conversion_rate) + # def get_adj_percent(self, obj): + # for d in getlist(obj.doclist, obj.fname): + # base_ref_rate = self.get_ref_rate(d.item_code, obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate) + # d.adj_rate = 0 + # d.ref_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate) + # d.basic_rate = flt(base_ref_rate) + # d.base_ref_rate = flt(base_ref_rate) + # d.export_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate) + # d.amount = flt(d.qty)*flt(base_ref_rate) + # d.export_amount = flt(d.qty)*flt(base_ref_rate)/flt(obj.doc.conversion_rate) - # Load Default Taxes - # ==================== - def load_default_taxes(self, obj): - if cstr(obj.doc.charge): - return self.get_other_charges(obj) - else: - return self.get_other_charges(obj, 1) - - - # Get other charges from Master - # ================================================================================= - def get_other_charges(self,obj, default=0): - obj.doclist = obj.doc.clear_table(obj.doclist, 'other_charges') - if not getlist(obj.doclist, 'other_charges'): - if default: add_cond = 'ifnull(t2.is_default,0) = 1' - else: add_cond = 't1.parent = "'+cstr(obj.doc.charge)+'"' - idx = 0 - other_charge = webnotes.conn.sql("""\ - select t1.* - from - `tabSales Taxes and Charges` t1, - `tabSales Taxes and Charges Master` t2 - where - t1.parent = t2.name and - t2.company = '%s' and - %s - order by t1.idx""" % (obj.doc.company, add_cond), as_dict=1) - from webnotes.model import default_fields - for other in other_charge: - # remove default fields like parent, parenttype etc. - # from query results - for field in default_fields: - if field in other: del other[field] - - d = addchild(obj.doc, 'other_charges', 'Sales Taxes and Charges', - obj.doclist) - d.fields.update(other) - d.rate = flt(d.rate) - d.tax_amount = flt(d.tax_rate) - d.included_in_print_rate = cint(d.included_in_print_rate) - d.idx = idx - idx += 1 - return obj.doclist + # # Load Default Taxes + # # ==================== + # def load_default_taxes(self, obj): + # if cstr(obj.doc.charge): + # return self.get_other_charges(obj) + # else: + # return self.get_other_charges(obj, 1) + # + # + # # Get other charges from Master + # # ================================================================================= + # def get_other_charges(self,obj, default=0): + # obj.doclist = obj.doc.clear_table(obj.doclist, 'other_charges') + # if not getlist(obj.doclist, 'other_charges'): + # if default: add_cond = 'ifnull(t2.is_default,0) = 1' + # else: add_cond = 't1.parent = "'+cstr(obj.doc.charge)+'"' + # idx = 0 + # other_charge = webnotes.conn.sql("""\ + # select t1.* + # from + # `tabSales Taxes and Charges` t1, + # `tabSales Taxes and Charges Master` t2 + # where + # t1.parent = t2.name and + # t2.company = '%s' and + # %s + # order by t1.idx""" % (obj.doc.company, add_cond), as_dict=1) + # from webnotes.model import default_fields + # for other in other_charge: + # # remove default fields like parent, parenttype etc. + # # from query results + # for field in default_fields: + # if field in other: del other[field] + # + # d = addchild(obj.doc, 'other_charges', 'Sales Taxes and Charges', + # obj.doclist) + # d.fields.update(other) + # d.rate = flt(d.rate) + # d.tax_amount = flt(d.tax_rate) + # d.included_in_print_rate = cint(d.included_in_print_rate) + # d.idx = idx + # idx += 1 + # return obj.doclist # Get TERMS AND CONDITIONS # ======================================================================================= @@ -327,23 +291,6 @@ class DocType(TransactionBase): } return ret - # Get Commission rate - # ======================================================================= - def get_comm_rate(self, sales_partner, obj): - - comm_rate = webnotes.conn.sql("select commission_rate from `tabSales Partner` where name = '%s' and docstatus != 2" %(sales_partner), as_dict=1) - if comm_rate: - total_comm = flt(comm_rate[0]['commission_rate']) * flt(obj.doc.net_total) / 100 - ret = { - 'commission_rate' : comm_rate and flt(comm_rate[0]['commission_rate']) or 0, - 'total_commission' : flt(total_comm) - } - return ret - else: - msgprint("Business Associate : %s does not exist in the system." % (sales_partner)) - raise Exception - - # To verify whether rate entered in details table does not exceed max discount % # ======================================================================================= def validate_max_discount(self,obj, detail_table): @@ -352,16 +299,6 @@ class DocType(TransactionBase): if discount and discount[0]['max_discount'] and (flt(d.adj_rate)>flt(discount[0]['max_discount'])): msgprint("You cannot give more than " + cstr(discount[0]['max_discount']) + " % discount on Item Code : "+cstr(d.item_code)) raise Exception - - - # Get sum of allocated % of sales person (it should be 100%) - # ======================================================================== - # it indicates % contribution of sales person in sales - def get_allocated_sum(self,obj): - 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) # =========================================================================== diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py index c74e7e1f9d..535a44ef65 100644 --- a/selling/doctype/sales_order/sales_order.py +++ b/selling/doctype/sales_order/sales_order.py @@ -83,12 +83,6 @@ class DocType(SellingController): def get_rate(self,arg): return get_obj('Sales Common').get_rate(arg) - def load_default_taxes(self): - self.doclist = get_obj('Sales Common').load_default_taxes(self) - - def get_other_charges(self): - self.doclist = get_obj('Sales Common').get_other_charges(self) - def get_tc_details(self): return get_obj('Sales Common').get_tc_details(self) @@ -225,7 +219,6 @@ class DocType(SellingController): sales_com_obj.check_conversion_rate(self) sales_com_obj.validate_max_discount(self,'sales_order_details') - sales_com_obj.get_allocated_sum(self) self.doclist = sales_com_obj.make_packing_list(self,'sales_order_details') if not self.doc.status: diff --git a/selling/doctype/sales_order/sales_order.txt b/selling/doctype/sales_order/sales_order.txt index 9780dc71a3..fdf60a21ec 100644 --- a/selling/doctype/sales_order/sales_order.txt +++ b/selling/doctype/sales_order/sales_order.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-06 12:03:43", + "creation": "2013-05-21 16:16:41", "docstatus": 0, - "modified": "2013-05-06 13:06:37", + "modified": "2013-05-21 18:30:14", "modified_by": "Administrator", "owner": "Administrator" }, @@ -230,7 +230,8 @@ "oldfieldname": "sales_order_details", "oldfieldtype": "Table", "options": "Sales Order Item", - "print_hide": 0 + "print_hide": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -434,7 +435,7 @@ "doctype": "DocField", "fieldname": "other_charges_total", "fieldtype": "Currency", - "label": "Taxes and Charges Total*", + "label": "Taxes and Charges Total", "oldfieldname": "other_charges_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -442,6 +443,15 @@ "read_only": 1, "width": "150px" }, + { + "doctype": "DocField", + "fieldname": "other_charges_total_export", + "fieldtype": "Currency", + "label": "Taxes and Charges Total (Export)", + "options": "company", + "print_hide": 1, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "other_charges_calculation", diff --git a/selling/doctype/sales_order_item/sales_order_item.txt b/selling/doctype/sales_order_item/sales_order_item.txt index fff2d080f3..6455807107 100644 --- a/selling/doctype/sales_order_item/sales_order_item.txt +++ b/selling/doctype/sales_order_item/sales_order_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:52", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-03-07 07:03:30", + "modified": "2013-05-21 16:44:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -109,6 +109,7 @@ "options": "currency", "print_hide": 1, "print_width": "70px", + "read_only": 1, "reqd": 0, "width": "70px" }, @@ -176,6 +177,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "read_only": 1, "reqd": 0, "width": "100px" }, diff --git a/selling/doctype/sales_team/sales_team.txt b/selling/doctype/sales_team/sales_team.txt index add466c334..29a951eef7 100644 --- a/selling/doctype/sales_team/sales_team.txt +++ b/selling/doctype/sales_team/sales_team.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:53", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-03-07 07:03:31", + "modified": "2013-05-21 17:04:45", "modified_by": "Administrator", "owner": "Administrator" }, @@ -42,6 +42,7 @@ "doctype": "DocField", "fieldname": "sales_designation", "fieldtype": "Data", + "hidden": 0, "label": "Designation", "oldfieldname": "sales_designation", "oldfieldtype": "Data", @@ -63,7 +64,7 @@ "doctype": "DocField", "fieldname": "allocated_percentage", "fieldtype": "Float", - "label": "Allocated (%)", + "label": "Contribution (%)", "oldfieldname": "allocated_percentage", "oldfieldtype": "Currency", "print_width": "100px", @@ -74,11 +75,12 @@ "doctype": "DocField", "fieldname": "allocated_amount", "fieldtype": "Currency", - "label": "Allocated Amount", + "label": "Contribution to Net Total", "oldfieldname": "allocated_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_width": "120px", + "read_only": 1, "reqd": 0, "width": "120px" }, diff --git a/selling/utils.py b/selling/utils.py index 23574dfa9e..5e986eeda7 100644 --- a/selling/utils.py +++ b/selling/utils.py @@ -17,7 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes import msgprint, _ -from webnotes.utils import flt +from webnotes.utils import flt, cint, comma_and import json def get_customer_list(doctype, txt, searchfield, start, page_len, filters): @@ -51,22 +51,45 @@ def get_item_details(args): args = json.loads(args) args = webnotes._dict(args) + if args.barcode: + args.item_code = _get_item_code(args.barcode) + item_bean = webnotes.bean("Item", args.item_code) _validate_item_details(args, item_bean.doc) out = _get_basic_details(args, item_bean) - if args.price_list_name and args.price_list_currency: - out.update(_get_price_list_rate(args, item_bean)) + meta = webnotes.get_doctype(args.doctype) + if meta.get_field("currency"): + out.base_ref_rate = out.basic_rate = out.ref_rate = out.export_rate = 0.0 + + if args.price_list_name and args.price_list_currency: + out.update(_get_price_list_rate(args, item_bean, meta)) if out.warehouse or out.reserved_warehouse: out.update(_get_available_qty(args, out.warehouse or out.reserved_warehouse)) out.customer_item_code = _get_customer_item_code(args, item_bean) + if cint(args.is_pos): + pos_settings = get_pos_settings(args.company) + out.update(apply_pos_settings(pos_settings, out)) + return out +def _get_item_code(barcode): + item_code = webnotes.conn.sql_list("""select name from `tabItem` where barcode=%s""", barcode) + + if not item_code: + msgprint(_("No Item found with Barcode") + ": %s" % barcode, raise_exception=True) + + elif len(item_code) > 1: + msgprint(_("Items") + " %s " % comma_and(item_code) + + _("have the same Barcode") + " %s" % barcode, raise_exception=True) + + return item_code[0] + def _validate_item_details(args, item): from utilities.transaction_base import validate_item_fetch validate_item_fetch(args, item) @@ -106,18 +129,21 @@ def _get_basic_details(args, item_bean): return out -def _get_price_list_rate(args, item_bean): +def _get_price_list_rate(args, item_bean, meta=None): base_ref_rate = item_bean.doclist.get({ "parentfield": "ref_rate_details", "price_list_name": args.price_list_name, "price_list_currency": args.price_list_currency, "selling": 1}) - out = webnotes._dict() - out.base_ref_rate = flt(base_ref_rate[0].ref_rate) if base_ref_rate else 0.0 - out.basic_rate = out.base_ref_rate - out.ref_rate = out.base_ref_rate / flt(args.conversion_rate) - out.export_rate = out.ref_rate - return out + + if not base_ref_rate: + return {} + + # found price list rate - now we can validate + from utilities.transaction_base import validate_currency + validate_currency(args, item_bean.doc, meta) + + return {"base_ref_rate": flt(base_ref_rate[0].ref_rate / args.plc_conversion_rate)} def _get_available_qty(args, warehouse): return webnotes.conn.get_value("Bin", {"item_code": args.item_code, "warehouse": warehouse}, @@ -128,4 +154,25 @@ def _get_customer_item_code(args, item_bean): "customer_name": args.customer}) return customer_item_code and customer_item_code[0].ref_code or None - \ No newline at end of file + +def get_pos_settings(company): + pos_settings = webnotes.conn.sql("""select * from `tabPOS Setting` where user = %s + and company = %s""", (webnotes.session['user'], company), as_dict=1) + + if not pos_settings: + pos_settings = webnotes.conn.sql("""select * from `tabPOS Setting` + where ifnull(user,'') = '' and company = %s""", company, as_dict=1) + + return pos_settings and pos_settings[0] or None + +def apply_pos_settings(pos_settings, opts): + out = {} + + for fieldname in ("income_account", "cost_center", "warehouse", "expense_account"): + if not opts.get(fieldname): + out[fieldname] = pos_settings.get(fieldname) + + if out.get("warehouse"): + out["actual_qty"] = _get_available_qty(opts, out.get("warehouse")).get("actual_qty") + + return out diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index 15e24ef293..93ca721f92 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -114,16 +114,6 @@ class DocType(SellingController): def get_rate(self,arg): return get_obj('Sales Common').get_rate(arg) - - def load_default_taxes(self): - self.doclist = get_obj('Sales Common').load_default_taxes(self) - - - def get_other_charges(self): - """Pull details from Sales Taxes and Charges Master""" - self.doclist = get_obj('Sales Common').get_other_charges(self) - - def so_required(self): """check in manage account if sales order required or not""" if webnotes.conn.get_value('Global Defaults', 'Global Defaults', 'so_required') == 'Yes': @@ -152,7 +142,6 @@ class DocType(SellingController): self.validate_warehouse() sales_com_obj.validate_max_discount(self, 'delivery_note_details') - sales_com_obj.get_allocated_sum(self) sales_com_obj.check_conversion_rate(self) # Set actual qty for each item in selected warehouse diff --git a/stock/doctype/delivery_note/delivery_note.txt b/stock/doctype/delivery_note/delivery_note.txt index 6f299efb15..72c6f47188 100644 --- a/stock/doctype/delivery_note/delivery_note.txt +++ b/stock/doctype/delivery_note/delivery_note.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-06 12:03:30", + "creation": "2013-05-21 16:16:31", "docstatus": 0, - "modified": "2013-05-06 13:08:13", + "modified": "2013-05-21 18:30:32", "modified_by": "Administrator", "owner": "Administrator" }, @@ -223,7 +223,8 @@ "oldfieldtype": "Table", "options": "Delivery Note Item", "print_hide": 0, - "read_only": 0 + "read_only": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -443,6 +444,15 @@ "read_only": 1, "width": "150px" }, + { + "doctype": "DocField", + "fieldname": "other_charges_total_export", + "fieldtype": "Currency", + "label": "Taxes and Charges Total (Export)", + "options": "company", + "print_hide": 1, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "calculate_charges", diff --git a/stock/doctype/delivery_note_item/delivery_note_item.txt b/stock/doctype/delivery_note_item/delivery_note_item.txt index 1073f0cdc2..c758fb5604 100644 --- a/stock/doctype/delivery_note_item/delivery_note_item.txt +++ b/stock/doctype/delivery_note_item/delivery_note_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-01 10:49:21", + "creation": "2013-04-19 13:31:02", "docstatus": 0, - "modified": "2013-04-17 17:20:58", + "modified": "2013-05-21 16:47:16", "modified_by": "Administrator", "owner": "Administrator" }, @@ -120,7 +120,7 @@ "options": "currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "reqd": 0, "width": "100px" }, @@ -189,7 +189,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "150px", - "read_only": 0, + "read_only": 1, "reqd": 0, "width": "150px" }, diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 540b385d51..d4c61f5690 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -297,14 +297,17 @@ def validate_item_fetch(args, item): if not args.company: msgprint(_("Please specify Company"), raise_exception=True) - # validate conversion rates - meta = webnotes.get_doctype(args.doctype) +def validate_currency(args, item, meta=None): + if not meta: + meta = webnotes.get_doctype(args.doctype) + + # validate conversion rate if meta.get_field("currency"): - # validate conversion rate validate_conversion_rate(args.currency, args.conversion_rate, meta.get_label("conversion_rate"), args.company) - # validate price list conversion rate - if args.price_list_name and args.price_list_currency: - validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate, - meta.get_label("plc_conversion_rate"), args.company) \ No newline at end of file + # validate price list conversion rate + if meta.get_field("price_list_currency") and args.price_list_name and \ + args.price_list_currency: + validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate, + meta.get_label("plc_conversion_rate"), args.company) \ No newline at end of file From 65d87555671618523bee5041b4ab9d2032e403ea Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 21 May 2013 19:53:33 +0530 Subject: [PATCH 10/17] [selling] [calculation] fixes --- controllers/selling_controller.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index a92ff88c48..01c8c61702 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -57,7 +57,8 @@ class SellingController(StockController): from selling.utils import get_item_details for item in self.doclist.get({"parentfield": "entries"}): if item.fields.get("item_code"): - ret = get_item_details(item.fields) + args = item.fields.copy().update(self.doc.fields) + ret = get_item_details(args) for fieldname, value in ret.items(): if self.meta.get_field(fieldname, parentfield="entries") and \ not item.fields.get(fieldname): @@ -319,7 +320,8 @@ class SellingController(StockController): self.doc.rounded_total_export = round(self.doc.grand_total_export) def calculate_commission(self): - if self.doc.commission_rate > 100: + self.round_floats_in(self.doc, ["net_total", "commission_rate"]) + if self.doc.commission_rate > 100.0: msgprint(_(self.meta.get_label("commission_rate")) + " " + _("cannot be greater than 100"), raise_exception=True) From 2168e39bf309e22df9174853508e1efa09336579 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 23 May 2013 19:25:08 +0530 Subject: [PATCH 11/17] [selling/buying] set dynamic labels --- .../purchase_common/purchase_common.js | 11 +- selling/doctype/sales_common/sales_common.js | 399 ++++++++++-------- 2 files changed, 225 insertions(+), 185 deletions(-) diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index fa2138afdc..b1ab40be12 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -151,7 +151,7 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ }, currency: function() { - this.set_dynamic_labels(); + this.price_list_currency(); }, company: function() { @@ -189,7 +189,8 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ field_label_map[fname] = label.trim() + " (" + currency + ")"; } }); - } + }; + setup_field_label_map(["net_total", "total_tax", "grand_total", "in_words", "other_charges_added", "other_charges_deducted", @@ -211,6 +212,9 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ this.frm.toggle_display(["conversion_rate", "net_total", "grand_total", "in_words", "other_charges_added", "other_charges_deducted"], this.frm.doc.currency != company_currency); + + this.frm.toggle_display(["plc_conversion_rate"], + this.frm.price_list_currency != company_currency) // set labels $.each(field_label_map, function(fname, label) { @@ -227,8 +231,9 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ $.each(fields_list, function(i, fname) { var docfield = wn.meta.get_docfield(grid_doctype, fname); if(docfield) { + var label = wn._((docfield.label || "")).replace(/\([^\)]*\)/g, ""); field_label_map[grid_doctype + "-" + fname] = - docfield.label + " (" + currency + ")"; + label.trim() + " (" + currency + ")"; } }); } diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js index 85e9fd7aab..fd3f5c58df 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -58,10 +58,8 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ // 2 refresh: function() { erpnext.hide_naming_series(); - this.toggle_price_list_fields(); - - // TODO - // display item wise taxes in an html table + this.show_item_wise_taxes(); + this.set_dynamic_labels(); }, // 3 @@ -75,7 +73,6 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ callback: function(r) { // remove this call when using client side mapper me.set_default_values(); - me.frm.refresh(); } }); @@ -134,15 +131,13 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ company: function() { if(this.frm.doc.company) { var me = this; - var company_currency = wn.model.get_doc(":Company", this.frm.doc.company).default_currency; + var company_currency = this.get_company_currency(); $.each(["currency", "price_list_currency"], function(i, fieldname) { if(!me.doc[fieldname]) { me.frm.set_value(fieldname, company_currency); - - // TODO - check this - me.frm.runclientscript(fieldname); } }); + this.price_list_currency(); } }, @@ -161,7 +156,7 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ freeze: true, callback: function(r) { if(!r.exc) { - me.frm.refresh(); + me.frm.refresh_fields(); if(me.frm.doc.price_list_name !== price_list_name) me.price_list_name(); } } @@ -172,9 +167,51 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ // TODO hide/unhide related fields }, - // TODO price_list_name: function() { - console.log("price_list_name"); + var me = this; + if(this.frm.doc.price_list_name) { + this.frm.call({ + method: "setup.utils.get_price_list_currency", + args: {args: { + price_list_name: this.frm.doc.price_list_name, + use_for: "selling" + }}, + callback: function(r) { + if(!r.exc) { + me.price_list_currency(); + } + } + }); + } + }, + + currency: function() { + this.price_list_currency(); + }, + + price_list_currency: function() { + // What TODO? should we make price list system non-mandatory? + // this.frm.toggle_reqd("plc_conversion_rate", + // !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); + + if(this.frm.doc.price_list_currency === this.get_company_currency()) { + this.frm.set_value("plc_conversion_rate", 1.0); + this.calculate_taxes_and_totals(); + } else if(this.frm.doc.price_list_currency === this.frm.doc.currency) { + this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate); + this.calculate_taxes_and_totals(); + } + + this.set_dynamic_labels(); + }, + + conversion_rate: function() { + this.price_list_currency(); + this.calculate_taxes_and_totals(); + }, + + plc_conversion_rate: function() { + this.price_list_currency(); }, ref_rate: function(doc, cdt, cdn) { @@ -214,6 +251,7 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ try { this.validate_on_previous_row(tax); this.validate_inclusive_tax(tax); + this.calculate_taxes_and_totals(); } catch(e) { tax.included_in_print_rate = 0; refresh_field("included_in_print_rate", tax.name, tax.parentfield); @@ -294,15 +332,6 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ }); }, - // TODO - toggle_price_list_fields: function() { - - }, - - set_dynamic_labels: function() { - - }, - calculate_taxes_and_totals: function() { this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); @@ -320,7 +349,6 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ this.calculate_commission(); this.calculate_contribution(); this._cleanup(); - this.frm.doc.in_words = this.frm.doc.in_words_export = ""; // TODO @@ -328,7 +356,8 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ // check for custom_recalc in custom scripts of server - this.frm.refresh(); + this.frm.refresh_fields(); + this.show_item_wise_taxes(); }, @@ -370,6 +399,7 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ // temporary fields tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0; + tax.tax_fraction_for_current_item = tax.grand_total_fraction_for_current_item = 0.0; me.validate_on_previous_row(tax); me.validate_inclusive_tax(tax); @@ -388,10 +418,10 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ tax.tax_fraction_for_current_item = me.get_current_tax_fraction(tax, item_tax_map); if(i==0) { - tax.grand_total_for_current_item = 1 + tax.tax_fraction_for_current_item; + tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item; } else { - tax.grand_total_for_current_item = - me.frm.tax_doclist[i-1].grand_total_for_current_item + + tax.grand_total_fraction_for_current_item = + me.frm.tax_doclist[i-1].grand_total_fraction_for_current_item + tax.tax_fraction_for_current_item; } @@ -422,18 +452,18 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ var current_tax_fraction = 0.0; if(cint(tax.included_in_print_rate)) { - var tax_rate = me._get_tax_rate(tax, item_tax_map); + var tax_rate = this._get_tax_rate(tax, item_tax_map); if(tax.charge_type == "On Net Total") { current_tax_fraction = (tax_rate / 100.0); } else if(tax.charge_type == "On Previous Row Amount") { current_tax_fraction = (tax_rate / 100.0) * - me.frm.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item; + this.frm.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item; } else if(tax.charge_type == "On Previous Row Total") { current_tax_fraction = (tax_rate / 100.0) * - me.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item; + this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item; } } @@ -512,15 +542,15 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ 0.0; } else if(tax.charge_type == "On Net Total") { - current_tax_amount = (tax_rate / 100.0); + current_tax_amount = (tax_rate / 100.0) * item.amount; } else if(tax.charge_type == "On Previous Row Amount") { current_tax_amount = (tax_rate / 100.0) * - me.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item; + this.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item; } else if(tax.charge_type == "On Previous Row Total") { current_tax_amount = (tax_rate / 100.0) * - me.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; + this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; } @@ -558,6 +588,7 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ }, calculate_contribution: function() { + var me = this; $.each(wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, {parentfield: "sales_team"}), function(i, sales_person) { wn.model.round_floats_in(sales_person); @@ -653,9 +684,162 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ _get_tax_rate: function(tax, item_tax_map) { return (keys(item_tax_map).indexOf(tax.account_head) != -1) ? - flt(item_tax_map.get(tax.account_head), precision("rate", tax)) : + flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; }, + + show_item_wise_taxes: function() { + $(this.get_item_wise_taxes_html()) + .appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty()); + }, + + get_item_wise_taxes_html: function() { + var item_tax = {}; + var tax_accounts = []; + var company_currency = this.get_company_currency(); + + $.each(this.get_tax_doclist(), function(i, tax) { + var tax_amount_precision = precision("tax_amount", tax); + $.each(JSON.parse(tax.item_wise_tax_detail || '{}'), + function(item_code, tax_amount) { + if(!item_tax[item_code]) item_tax[item_code] = {}; + item_tax[item_code][tax.account_head] = flt(tax_amount, tax_amount_precision); + }); + tax_accounts.push(tax.account_head); + }); + + var headings = $.map([wn._("Item Name")].concat(tax_accounts), + function(head) { return '' + (head || "") + "" }).join("\n"); + + var rows = $.map(this.get_item_doclist(), function(item) { + var item_tax_record = item_tax[item.item_code || item.item_name]; + return repl("%(item_name)s%(taxes)s", { + item_name: item.item_name, + taxes: $.map(tax_accounts, function(head) { + return "" + format_currency(item_tax_record[head], company_currency) + "" + }).join("\n") + }); + }).join("\n"); + + return '
\ + ' + headings + ' \ + ' + rows + ' \ +
'; + }, + + get_charges: function() { + var me = this; + if(this.frm.doc.charge) { + this.frm.call({ + doc: this.frm.doc, + method: "get_other_charges", + callback: function(r) { + if(!r.exc) { + me.calculate_taxes_and_totals(); + } + } + }); + } + }, + + set_dynamic_labels: function() { + var company_currency = this.get_company_currency(); + + this.change_form_labels(company_currency); + this.change_grid_labels(company_currency); + }, + + change_form_labels: function(company_currency) { + var me = this; + var field_label_map = {}; + + var setup_field_label_map = function(fields_list, currency) { + $.each(fields_list, function(i, fname) { + var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname); + if(docfield) { + var label = wn._((docfield.label || "")).replace(/\([^\)]*\)/g, ""); + field_label_map[fname] = label.trim() + " (" + currency + ")"; + } + }); + }; + + setup_field_label_map(["net_total", "other_charges_total", "grand_total", + "rounded_total", "in_words", + "outstanding_amount", "total_advance", "paid_amount", "write_off_amount"], + company_currency); + + setup_field_label_map(["net_total_export", "other_charges_total_export", "grand_total_export", + "rounded_total_export", "in_words_export"], this.frm.doc.currency); + + setup_field_label_map(["conversion_rate"], "1 " + this.frm.doc.currency + + " = [?] " + company_currency); + + if(this.frm.doc.price_list_currency && this.frm.doc.price_list_currency!=company_currency) { + setup_field_label_map(["plc_conversion_rate"], "1 " + this.frm.doc.price_list_currency + + " = [?] " + company_currency); + } + + // toggle fields + this.frm.toggle_display(["conversion_rate", "net_total", "other_charges_total", + "grand_total", "rounded_total", "in_words"], + this.frm.doc.currency != company_currency); + + this.frm.toggle_display(["plc_conversion_rate"], + this.frm.doc.price_list_currency != company_currency); + + // set labels + $.each(field_label_map, function(fname, label) { + me.frm.fields_dict[fname].set_label(label); + }); + }, + + change_grid_labels: function(company_currency) { + var me = this; + var field_label_map = {}; + + var setup_field_label_map = function(fields_list, currency, parentfield) { + var grid_doctype = me.frm.fields_dict[parentfield].grid.doctype; + $.each(fields_list, function(i, fname) { + var docfield = wn.meta.get_docfield(grid_doctype, fname); + if(docfield) { + var label = wn._((docfield.label || "")).replace(/\([^\)]*\)/g, ""); + field_label_map[grid_doctype + "-" + fname] = + label.trim() + " (" + currency + ")"; + } + }); + } + + setup_field_label_map(["basic_rate", "base_ref_rate", "amount"], + company_currency, this.fname); + + setup_field_label_map(["export_rate", "ref_rate", "export_amount"], + this.frm.doc.currency, this.fname); + + setup_field_label_map(["tax_amount", "total"], company_currency, "other_charges"); + + if(this.frm.fields_dict["advance_allocation_details"]) { + setup_field_label_map(["advance_amount", "allocated_amount"], company_currency, + "advance_allocation_details"); + } + + // toggle columns + var item_grid = this.frm.fields_dict[this.fname].grid; + var show = this.frm.doc.currency != company_currency; + $.each(["basic_rate", "base_ref_rate", "amount"], function(i, fname) { + if(wn.meta.get_docfield(item_grid.doctype, fname)) + item_grid.set_column_disp(fname, show); + }); + + // set labels + var $wrapper = $(this.frm.wrapper); + $.each(field_label_map, function(fname, label) { + $wrapper.find('[data-grid-fieldname="'+fname+'"]').text(label); + }); + }, + + get_company_currency: function() { + return erpnext.get_currency(this.frm.doc.company); + } }); // to save previous state of cur_frm.cscript @@ -667,61 +851,6 @@ cur_frm.cscript = new erpnext.selling.SellingController({frm: cur_frm}); // for backward compatibility: combine new and previous states $.extend(cur_frm.cscript, prev_cscript); - -var set_dynamic_label_par = function(doc, cdt, cdn, base_curr) { - //parent flds - par_cols_base = {'net_total': 'Net Total', 'other_charges_total': 'Taxes and Charges Total', - 'grand_total': 'Grand Total', 'rounded_total': 'Rounded Total', 'in_words': 'In Words'} - par_cols_export = {'grand_total_export': 'Grand Total', 'rounded_total_export': 'Rounded Total', 'in_words_export': 'In Words'}; - - for (d in par_cols_base) cur_frm.fields_dict[d].label_span.innerHTML = par_cols_base[d]+' (' + base_curr + ')'; - for (d in par_cols_export) cur_frm.fields_dict[d].label_span.innerHTML = par_cols_export[d]+' (' + doc.currency + ')'; - cur_frm.fields_dict['conversion_rate'].label_span.innerHTML = "Conversion Rate (" + doc.currency +' -> '+ base_curr + ')'; - cur_frm.fields_dict['plc_conversion_rate'].label_span.innerHTML = 'Price List Currency Conversion Rate (' + doc.price_list_currency +' -> '+ base_curr + ')'; - - if (doc.doctype == 'Sales Invoice') { - si_cols = {'total_advance': 'Total Advance', 'outstanding_amount': 'Outstanding Amount', 'paid_amount': 'Paid Amount', 'write_off_amount': 'Write Off Amount'} - for (d in si_cols) cur_frm.fields_dict[d].label_span.innerHTML = si_cols[d] + ' (' + base_curr + ')'; - } -} - - -var set_dynamic_label_child = function(doc, cdt, cdn, base_curr) { - // item table flds - item_cols_base = {'basic_rate': 'Basic Rate', 'base_ref_rate': 'Price List Rate', 'amount': 'Amount'}; - item_cols_export = {'export_rate': 'Basic Rate', 'ref_rate': 'Price List Rate', 'export_amount': 'Amount'}; - - for (d in item_cols_base) $('[data-grid-fieldname="'+cur_frm.cscript.tname+'-'+d+'"]').html(item_cols_base[d]+' ('+base_curr+')'); - for (d in item_cols_export) $('[data-grid-fieldname="'+cur_frm.cscript.tname+'-'+d+'"]').html(item_cols_export[d]+' ('+doc.currency+')'); - - var hide = (doc.currency == sys_defaults['currency']) ? false : true; - for (f in item_cols_base) { - cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp(f, hide); - } - - //tax table flds - tax_cols = {'tax_amount': 'Amount', 'total': 'Total'}; - for (d in tax_cols) $('[data-grid-fieldname="Sales Taxes and Charges-'+d+'"]').html(tax_cols[d]+' ('+base_curr+')'); - - if (doc.doctype == 'Sales Invoice') { - // advance table flds - adv_cols = {'advance_amount': 'Advance Amount', 'allocated_amount': 'Allocated Amount'} - for (d in adv_cols) $('[data-grid-fieldname="Sales Invoice Advance-'+d+'"]').html(adv_cols[d]+' ('+base_curr+')'); - } -} - -// Change label dynamically based on currency -//------------------------------------------------------------------ - -cur_frm.cscript.dynamic_label = function(doc, cdt, cdn, base_curr, callback) { - cur_frm.cscript.base_currency = base_curr; - set_dynamic_label_par(doc, cdt, cdn, base_curr); - set_dynamic_label_child(doc, cdt, cdn, base_curr); - set_sales_bom_help(doc); - - if (callback) callback(doc, cdt, cdn); -} - // Help for Sales BOM items var set_sales_bom_help = function(doc) { if(!cur_frm.fields_dict.packing_list) return; @@ -746,88 +875,6 @@ var set_sales_bom_help = function(doc) { refresh_field('sales_bom_help'); } - -// hide / unhide price list currency based on availability of price list in customer's currency -//--------------------------------------------------------------------------------------------------- - -// cur_frm.cscript.hide_price_list_currency = function(doc, cdt, cdn, callback1) { -// if (doc.price_list_name && doc.currency) { -// wn.call({ -// method: 'selling.doctype.sales_common.sales_common.get_price_list_currency', -// args: {'price_list':doc.price_list_name, 'company': doc.company}, -// callback: function(r, rt) { -// pl_currency = r.message[0]?r.message[0]:[]; -// unhide_field(['price_list_currency', 'plc_conversion_rate']); -// -// if (pl_currency.length==1) { -// if (doc.price_list_currency != pl_currency[0]) -// set_multiple(cdt, cdn, {price_list_currency:pl_currency[0]}); -// if (pl_currency[0] == doc.currency) { -// if(doc.plc_conversion_rate != doc.conversion_rate) -// set_multiple(cdt, cdn, {plc_conversion_rate:doc.conversion_rate}); -// hide_field(['price_list_currency', 'plc_conversion_rate']); -// } else if (pl_currency[0] == r.message[1]) { -// if (doc.plc_conversion_rate != 1) -// set_multiple(cdt, cdn, {plc_conversion_rate:1}) -// hide_field(['price_list_currency', 'plc_conversion_rate']); -// } -// } -// -// if (r.message[1] == doc.currency) { -// if (doc.conversion_rate != 1) -// set_multiple(cdt, cdn, {conversion_rate:1}); -// hide_field(['conversion_rate', 'grand_total_export', 'in_words_export', 'rounded_total_export']); -// } else { -// unhide_field(['conversion_rate', 'grand_total_export', 'in_words_export']); -// if(!cint(sys_defaults.disable_rounded_total)) -// unhide_field("rounded_total_export"); -// } -// if (r.message[1] == doc.price_list_currency) { -// if (doc.plc_conversion_rate != 1) -// set_multiple(cdt, cdn, {plc_conversion_rate:1}); -// hide_field('plc_conversion_rate'); -// } else unhide_field('plc_conversion_rate'); -// cur_frm.cscript.dynamic_label(doc, cdt, cdn, r.message[1], callback1); -// } -// }) -// } -// } - - -// TRIGGERS FOR CALCULATIONS -// ===================================================================================================== - -// ********************* CURRENCY ****************************** -cur_frm.cscript.currency = function(doc, cdt, cdn) { - cur_frm.cscript.price_list_name(doc, cdt, cdn); -} - -cur_frm.cscript.price_list_currency = cur_frm.cscript.currency; -cur_frm.cscript.conversion_rate = cur_frm.cscript.currency; -cur_frm.cscript.plc_conversion_rate = cur_frm.cscript.currency; - - -// ******************** PRICE LIST ****************************** -cur_frm.cscript.price_list_name = function(doc, cdt, cdn) { - var callback = function() { - var fname = cur_frm.cscript.fname; - var cl = getchildren(cur_frm.cscript.tname, doc.name, cur_frm.cscript.fname); - if(doc.price_list_name && doc.currency && doc.price_list_currency && doc.conversion_rate && doc.plc_conversion_rate) { - $c_obj(make_doclist(doc.doctype, doc.name), 'get_adj_percent', '', - function(r, rt) { - refresh_field(fname); - var doc = locals[cdt][cdn]; - cur_frm.cscript.recalc(doc,3); //this is to re-calculate BASIC RATE and AMOUNT on basis of changed REF RATE - } - ); - } - } - cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn, callback); -} - - - -// ******************** ITEM CODE ******************************** cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field("item_code").get_query = function(doc, cdt, cdn) { if (doc.order_type == "Maintenance") { return erpnext.queries.item({ @@ -894,16 +941,4 @@ cur_frm.fields_dict.charge.get_query = function(doc) { ORDER BY `tabSales Taxes and Charges Master`.name LIMIT 50'; } -// ********************* Get Charges **************************** -cur_frm.cscript.get_charges = function(doc, cdt, cdn, callback) { - $c_obj(make_doclist(doc.doctype,doc.name), - 'get_other_charges', - '', - function(r, rt) { - cur_frm.cscript.calculate_charges(doc, cdt, cdn); - if(callback) callback(doc, cdt, cdn); - }, null,null,cur_frm.fields_dict.get_charges.input); -} - - cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query; \ No newline at end of file From 3543f3004692164e7405937ffd7d94165ca8fa3f Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 24 May 2013 19:25:01 +0530 Subject: [PATCH 12/17] [calculations] [client/server] first cut --- .../purchase_invoice/purchase_invoice.js | 55 --- .../purchase_invoice/purchase_invoice.py | 30 +- .../purchase_invoice/test_purchase_invoice.py | 58 +-- .../sales_invoice/test_sales_invoice.py | 22 +- .../purchase_common/purchase_common.js | 238 +++++---- .../purchase_common/purchase_common.py | 11 - .../doctype/purchase_order/purchase_order.js | 46 -- .../doctype/purchase_order/purchase_order.py | 5 - .../supplier_quotation/supplier_quotation.js | 33 +- buying/utils.py | 5 +- controllers/accounts_controller.py | 248 +++++++++- controllers/buying_controller.py | 259 ++-------- controllers/selling_controller.py | 242 +-------- public/js/transaction.js | 443 +++++++++++++++++ selling/doctype/quotation/quotation.js | 8 - selling/doctype/sales_common/sales_common.js | 463 ++---------------- selling/doctype/sales_order/sales_order.js | 17 +- stock/doctype/delivery_note/delivery_note.js | 9 - .../material_request/material_request.js | 14 - .../material_request/material_request.py | 13 - .../purchase_receipt/purchase_receipt.js | 14 - 21 files changed, 958 insertions(+), 1275 deletions(-) create mode 100644 public/js/transaction.js diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index b498ce25af..3ddf907fdd 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -34,67 +34,12 @@ erpnext.buying.PurchaseInvoiceController = erpnext.buying.BuyingController.exten cur_frm.cscript.is_opening(doc); }, - onload_post_render: function(doc, dt, dn) { - var me = this; - var callback1 = function(doc, dt, dn) { - var callback2 = function(doc, dt, dn) { - if(doc.__islocal && doc.supplier) cur_frm.cscript.supplier(doc, dt, dn); - } - me.update_item_details(doc, dt, dn, callback2); - } - - // TODO: improve this - if(this.frm.doc.__islocal) { - if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) { - this.price_list_name(callback1); - } else { - callback1(doc, dt, dn); - } - } - } }); // for backward compatibility: combine new and previous states $.extend(cur_frm.cscript, new erpnext.buying.PurchaseInvoiceController({frm: cur_frm})); -cur_frm.cscript.onload = function(doc,dt,dn) { - if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()}); -} - -cur_frm.cscript.supplier = function(doc,dt,dn) { - var callback = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - get_server_fields('get_credit_to','','',doc, dt, dn, 0, callback2); - } - - var callback2 = function(r,rt){ - var doc = locals[cur_frm.doctype][cur_frm.docname]; - var el = getchildren('Purchase Invoice Item',doc.name,'entries'); - for(var i in el){ - if(el[i].item_code && (!el[i].expense_head || !el[i].cost_center)){ - args = { - item_code: el[i].item_code, - expense_head: el[i].expense_head, - cost_center: el[i].cost_center - }; - get_server_fields('get_default_values', JSON.stringify(args), 'entries', doc, el[i].doctype, el[i].name, 1); - } - } - cur_frm.cscript.calc_amount(doc, 1); - } - - if (doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }),'', doc, dt, dn, 1, function(doc, dt, dn) { - cur_frm.refresh(); - callback(doc, dt, dn); - }); - unhide_field(['supplier_address','contact_person']); - } - -} - cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.supplier) get_server_fields('get_supplier_address', JSON.stringify({supplier: doc.supplier, address: doc.supplier_address, contact: doc.contact_person}),'', doc, dt, dn, 1); } diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index b63a176eba..7f8ad63a9e 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -91,6 +91,11 @@ class DocType(BuyingController): msgprint("%s does not have an Account Head in %s. You must first create it from the Supplier Master" % (self.doc.supplier, self.doc.company)) return ret + def set_supplier_defaults(self): + # TODO cleanup these methods + self.doc.fields.update(self.get_credit_to()) + super(DocType, self).set_supplier_defaults() + def get_cust(self): ret = {} if self.doc.credit_to: @@ -100,31 +105,6 @@ class DocType(BuyingController): return ret - def get_default_values(self, args): - if isinstance(args, basestring): - import json - args = json.loads(args) - - out = webnotes._dict() - - item = webnotes.conn.sql("""select name, purchase_account, cost_center, - is_stock_item from `tabItem` where name=%s""", args.get("item_code"), as_dict=1) - - if item and item[0]: - item = item[0] - - if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) and \ - item.is_stock_item == "Yes": - # unset expense head for stock item and auto inventory accounting - out.expense_head = out.cost_center = None - else: - if not args.get("expense_head"): - out.expense_head = item.purchase_account - if not args.get("cost_center"): - out.cost_center = item.cost_center - - return out - def pull_details(self): if self.doc.purchase_receipt_main: self.validate_duplicate_docname('purchase_receipt') diff --git a/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/accounts/doctype/purchase_invoice/test_purchase_invoice.py index a70c932d9b..ebd44d4a60 100644 --- a/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -119,25 +119,6 @@ class TestPurchaseInvoice(unittest.TestCase): wrapper.insert() wrapper.load_from_db() - self.assertEqual(wrapper.doclist[0].net_total, 1250) - - # tax amounts - expected_values = [ - ["_Test Account Shipping Charges - _TC", 100, 1350], - ["_Test Account Customs Duty - _TC", 125, 1350], - ["_Test Account Excise Duty - _TC", 140, 1490], - ["_Test Account Education Cess - _TC", 2.8, 1492.8], - ["_Test Account S&H Education Cess - _TC", 1.4, 1494.2], - ["_Test Account CST - _TC", 29.88, 1524.08], - ["_Test Account VAT - _TC", 156.25, 1680.33], - ["_Test Account Discount - _TC", 168.03, 1512.30], - ] - - for i, tax in enumerate(wrapper.doclist.get({"parentfield": "purchase_tax_details"})): - self.assertEqual(tax.account_head, expected_values[i][0]) - self.assertEqual(tax.tax_amount, expected_values[i][1]) - self.assertEqual(tax.total, expected_values[i][2]) - expected_values = [ ["_Test Item Home Desktop 100", 90, 59], ["_Test Item Home Desktop 200", 135, 177] @@ -147,13 +128,41 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(item.item_tax_amount, expected_values[i][1]) self.assertEqual(item.valuation_rate, expected_values[i][2]) + self.assertEqual(wrapper.doclist[0].net_total, 1250) + + # tax amounts + expected_values = [ + ["_Test Account Shipping Charges - _TC", 100, 1350], + ["_Test Account Customs Duty - _TC", 125, 1350], + ["_Test Account Excise Duty - _TC", 140, 1490], + ["_Test Account Education Cess - _TC", 2.8, 1492.8], + ["_Test Account S&H Education Cess - _TC", 1.4, 1494.2], + ["_Test Account CST - _TC", 29.88, 1524.08], + ["_Test Account VAT - _TC", 156.25, 1680.33], + ["_Test Account Discount - _TC", 168.03, 1512.30], + ] + + for i, tax in enumerate(wrapper.doclist.get({"parentfield": "purchase_tax_details"})): + self.assertEqual(tax.account_head, expected_values[i][0]) + self.assertEqual(tax.tax_amount, expected_values[i][1]) + self.assertEqual(tax.total, expected_values[i][2]) + def test_purchase_invoice_with_subcontracted_item(self): wrapper = webnotes.bean(copy=test_records[0]) wrapper.doclist[1].item_code = "_Test FG Item" wrapper.run_method("calculate_taxes_and_totals") wrapper.insert() wrapper.load_from_db() - + + expected_values = [ + ["_Test FG Item", 90, 7059], + ["_Test Item Home Desktop 200", 135, 177] + ] + for i, item in enumerate(wrapper.doclist.get({"parentfield": "entries"})): + self.assertEqual(item.item_code, expected_values[i][0]) + self.assertEqual(item.item_tax_amount, expected_values[i][1]) + self.assertEqual(item.valuation_rate, expected_values[i][2]) + self.assertEqual(wrapper.doclist[0].net_total, 1250) # tax amounts @@ -172,15 +181,6 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(tax.account_head, expected_values[i][0]) self.assertEqual(tax.tax_amount, expected_values[i][1]) self.assertEqual(tax.total, expected_values[i][2]) - - expected_values = [ - ["_Test FG Item", 90, 7059], - ["_Test Item Home Desktop 200", 135, 177] - ] - for i, item in enumerate(wrapper.doclist.get({"parentfield": "entries"})): - self.assertEqual(item.item_code, expected_values[i][0]) - self.assertEqual(item.item_tax_amount, expected_values[i][1]) - self.assertEqual(item.valuation_rate, expected_values[i][2]) def test_purchase_invoice_with_advance(self): from accounts.doctype.journal_voucher.test_journal_voucher \ diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index 505848a3c8..7c4b8b1ea4 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -110,8 +110,8 @@ class TestSalesInvoice(unittest.TestCase): for i, tax in enumerate(si.doclist.get({"parentfield": "other_charges"})): tax.idx = i+1 - si.doclist[1].export_rate = 62.5 - si.doclist[1].export_rate = 191 + si.doclist[1].ref_rate = 62.5 + si.doclist[1].ref_rate = 191 for i in [3, 5, 6, 7, 8, 9]: si.doclist[i].included_in_print_rate = 1 @@ -173,9 +173,9 @@ class TestSalesInvoice(unittest.TestCase): si = webnotes.bean(copy=test_records[3]) si.doc.currency = "USD" si.doc.conversion_rate = 50 - si.doclist[1].export_rate = 50 + si.doclist[1].ref_rate = 55.56 si.doclist[1].adj_rate = 10 - si.doclist[2].export_rate = 150 + si.doclist[2].ref_rate = 187.5 si.doclist[2].adj_rate = 20 si.doclist[9].rate = 5000 @@ -317,7 +317,7 @@ class TestSalesInvoice(unittest.TestCase): pos[0]["cash_bank_account"] = "_Test Account Bank Account - _TC" pos[0]["paid_amount"] = 600.0 - si = webnotes.bean(pos) + si = webnotes.bean(copy=pos) si.insert() si.submit() @@ -340,11 +340,11 @@ class TestSalesInvoice(unittest.TestCase): expected_gl_entries = 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], + [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, 375.0], - [test_records[1][1]["expense_account"], 375.0, 0.0], + [pos[1]["expense_account"], 375.0, 0.0], [si.doc.debit_to, 0.0, 600.0], ["_Test Account Bank Account - _TC", 600.0, 0.0] ]) @@ -760,6 +760,7 @@ test_records = [ "item_code": "_Test Item Home Desktop 100", "item_name": "_Test Item Home Desktop 100", "qty": 10, + "ref_rate": 50, "export_rate": 50, "stock_uom": "_Test UOM", "item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}), @@ -773,6 +774,7 @@ test_records = [ "item_code": "_Test Item Home Desktop 200", "item_name": "_Test Item Home Desktop 200", "qty": 5, + "ref_rate": 150, "export_rate": 150, "stock_uom": "_Test UOM", "income_account": "Sales - _TC", @@ -883,6 +885,7 @@ test_records = [ "item_code": "_Test Item Home Desktop 100", "item_name": "_Test Item Home Desktop 100", "qty": 10, + "ref_rate": 62.5, "export_rate": 62.5, "stock_uom": "_Test UOM", "item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}), @@ -896,6 +899,7 @@ test_records = [ "item_code": "_Test Item Home Desktop 200", "item_name": "_Test Item Home Desktop 200", "qty": 5, + "ref_rate": 190.66, "export_rate": 190.66, "stock_uom": "_Test UOM", "income_account": "Sales - _TC", diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index b1ab40be12..320212254f 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -20,8 +20,9 @@ // cur_frm.cscript.fname - Details fieldname wn.provide("erpnext.buying"); +wn.require("app/js/transaction.js"); -erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ +erpnext.buying.BuyingController = erpnext.TransactionController.extend({ setup: function() { var me = this; @@ -42,50 +43,39 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ } }, - refresh: function() { - this.frm.clear_custom_buttons(); - erpnext.hide_naming_series(); + validate: function() { - if(this.frm.fields_dict.supplier) - this.frm.toggle_display("contact_section", this.frm.doc.supplier); - - if(this.frm.fields_dict.currency) - this.set_dynamic_labels(); }, - price_list_name: function(callback_fn) { - var me = this; - - if(this.frm.doc.price_list_name) { - if(!this.frm.doc.price_list_currency) { - // set price list currency + supplier: function() { + if(this.frm.doc.supplier || this.frm.doc.credit_to) { + if(!this.frm.doc.company) { + this.frm.set_value("supplier", null); + msgprint(wn._("Please specify Company")); + } else { + var me = this; + var price_list_name = this.frm.doc.price_list_name; + this.frm.call({ - method: "setup.utils.get_price_list_currency", - args: {args: { - price_list_name: this.frm.doc.price_list_name, - use_for: "buying" - }}, + doc: this.frm.doc, + method: "set_supplier_defaults", + freeze: true, callback: function(r) { if(!r.exc) { - me.price_list_currency(); - if (typeof callback_fn === "function") - callback_fn(me.frm.doc, me.frm.doc.doctype, me.frm.doc.name); + me.frm.refresh_fields(); + if(me.frm.doc.price_list_name !== price_list_name) me.price_list_name(); } } }); - } else { - me.price_list_currency(); - if (typeof callback_fn === "function") - callback_fn(me.frm.doc, me.frm.doc.doctype, me.frm.doc.name); } - } - }, + } + } item_code: function(doc, cdt, cdn) { var me = this; var item = wn.model.get_doc(cdt, cdn); if(item.item_code) { - if(!this.validate_company_and_party()) { + if(!this.validate_company_and_party("supplier")) { item.item_code = null; refresh_field("item_code", item.name, item.parentfield); } else { @@ -109,65 +99,115 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ } }, callback: function(r) { - // TODO: calculate + if(!r.exc) { + me.ref_rate(me.frm.doc, cdt, cdn); + } } }); } } }, - validate_company_and_party: function() { - var valid = true; - $.each(["company", "supplier"], function(i, fieldname) { - if(!me.frm.doc[fieldname]) { - valid = false; - msgprint(wn._("Please specify") + ": " + - wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + - ". " + wn._("It is needed to fetch Item Details.")); - } - }); - return valid; + price_list_name: function(callback_fn) { + this._super("buying"); }, - update_item_details: function(doc, dt, dn, callback) { - if(!this.frm.doc.__islocal) return; - + calculate_taxes_and_totals: function() { + this._super(); + this.calculate_outstanding_amount(); + this.frm.refresh_fields(); + }, + + calculate_item_values: function() { var me = this; - var children = getchildren(this.tname, this.frm.doc.name, this.fname); - if(children && children.length) { - this.frm.call({ - doc: me.frm.doc, - method: "update_item_details", - callback: function(r) { - if(!r.exc) { - refresh_field(me.fname); - me.load_defaults(me.frm.doc, dt, dn, callback); - } - } - }) - } else { - this.load_taxes(doc, dt, dn, callback); + + if(this.frm.doc.doctype != "Purchase Invoice") { + wn.meta.docfield_map[this.tname]["rate"] = $.extend({}, + wn.meta.docfield_map[this.tname]["purchase_rate"]); + } + + $.each(this.frm.item_doclist, function(i, item) { + if(me.frm.doc.doctype != "Purchase Invoice") { + item.rate = item.purchase_rate; + } + + wn.model.round_floats_in(item); + item.import_amount = flt(item.import_rate * item.qty, precision("import_amount", item)); + item.item_tax_amount = 0.0; + + me._set_in_company_currency(item, "import_ref_rate", "purchase_ref_rate"); + me._set_in_company_currency(item, "import_rate", "rate"); + me._set_in_company_currency(item, "import_amount", "amount"); + }); + }, + + calculate_net_total: function() { + var me = this; + + this.frm.doc.net_total = this.frm.doc.net_total_import = 0.0; + $.each(this.frm.item_doclist, function(i, item) { + me.frm.doc.net_total += item.amount; + me.frm.doc.net_total_import += item.import_amount; + }); + + wn.model.round_floats_in(this.frm.doc, ["net_total", "net_total_import"]); + }, + + calculate_totals: function() { + var tax_count = this.frm.tax_doclist.length; + this.frm.doc.grand_total = flt( + tax_count ? this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total, + precision("grand_total")); + this.frm.doc.grand_total_import = flt(this.frm.doc.grand_total / this.frm.doc.conversion_rate, + precision("grand_total_import")); + + this.frm.doc.total_tax = flt(this.frm.doc.grand_total - this.frm.doc.net_total, + precision("total_tax")); + + if(wn.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) { + this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total); + } + + if(wn.meta.get_docfield(this.frm.doc.doctype, "rounded_total_import", this.frm.doc.name)) { + this.frm.doc.rounded_total_import = Math.round(this.frm.doc.grand_total_import); } }, - currency: function() { - this.price_list_currency(); + _cleanup: function() { + this._super(); + this.frm.doc.in_words = this.frm.doc.in_words_import = ""; + + // except in purchase invoice, rate field is purchase_rate + // reset fieldname of rate + if(this.frm.doc.doctype != "Purchase Invoice") { + delete wn.meta.docfield_map[this.tname]["rate"]; + + $.each(this.frm.item_doclist, function(i, item) { + item.purchase_rate = item.rate; + delete item["rate"]; + }); + } }, - company: function() { - this.set_dynamic_labels(); + calculate_outstanding_amount: function() { + if(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.docstatus < 2) { + wn.model.round_floats_in(this.frm.doc, ["total_advance", "write_off_amount"]); + this.frm.doc.total_amount_to_pay = flt(this.frm.doc.grand_total - this.frm.doc.write_off_amount, + precision("total_amount_to_pay")); + this.frm.doc.outstanding_amount = flt(this.frm.doc.total_amount_to_pay - this.frm.doc.total_advance, + precision("outstanding_amount")); + } }, - price_list_currency: function() { - this.frm.toggle_reqd("plc_conversion_rate", - !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); - - this.set_dynamic_labels(); - - if(this.frm.doc.price_list_currency === this.get_company_currency()) - this.frm.set_value("plc_conversion_rate", 1.0); - else if(this.frm.doc.price_list_currency === this.frm.doc.currency) - this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate || 1.0); + set_item_tax_amount: function(item, tax, current_tax_amount) { + // item_tax_amount is the total tax amount applied on that item + // stored for valuation + // + // TODO: rename item_tax_amount to valuation_tax_amount + if(["Valuation", "Valuation and Total"].indexOf(tax.category) != -1) { + // accumulate only if tax is for Valuation / Valuation and Total + item.item_tax_amount += flt(current_tax_amount, precision("item_tax_amount", item)); + } }, set_dynamic_labels: function(doc, dt, dn) { @@ -265,10 +305,6 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ $wrapper.find('[data-grid-fieldname="'+fname+'"]').text(label); }); }, - - get_company_currency: function() { - return erpnext.get_currency(this.frm.doc.company); - } }); // to save previous state of cur_frm.cscript @@ -284,54 +320,6 @@ $.extend(cur_frm.cscript, prev_cscript); var tname = cur_frm.cscript.tname; var fname = cur_frm.cscript.fname; -cur_frm.cscript.get_default_schedule_date = function(doc) { - var ch = getchildren( tname, doc.name, fname); - if (flt(ch.length) > 0){ - $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_schedule_date', '', function(r, rt) { refresh_field(fname); }); - } -} - -cur_frm.cscript.load_taxes = function(doc, cdt, cdn, callback) { - // run if this is not executed from dt_map... - doc = locals[doc.doctype][doc.name]; - if(doc.supplier || getchildren('Purchase Taxes and Charges', doc.name, 'purchase_tax_details', doc.doctype).length) { - if(callback) { - callback(doc, cdt, cdn); - } - } else { - $c_obj(make_doclist(doc.doctype, doc.name),'load_default_taxes','',function(r,rt){ - refresh_field('purchase_tax_details'); - if(callback) callback(doc, cdt, cdn); - }); - } -} - - - -// Gets called after existing item details are update to fill in -// remaining default values -cur_frm.cscript.load_defaults = function(doc, dt, dn, callback) { - if(!cur_frm.doc.__islocal) { return; } - - doc = locals[doc.doctype][doc.name]; - var fields_to_refresh = wn.model.set_default_values(doc); - if(fields_to_refresh) { refresh_many(fields_to_refresh); } - - fields_to_refresh = null; - var children = getchildren(cur_frm.cscript.tname, doc.name, cur_frm.cscript.fname); - if(!children) { return; } - for(var i=0; i now())" % cstr(d.item_code) , as_dict = 1) - ltd = item and cint(item[0]['lead_time_days']) or 0 - if ltd and obj.doc.transaction_date: - if d.fields.has_key('lead_time_date') or obj.doc.doctype == 'Material Request': - d.lead_time_date = cstr(add_days( obj.doc.transaction_date, cint(ltd))) - if not d.fields.has_key('prevdoc_docname') or (d.fields.has_key('prevdoc_docname') and not d.prevdoc_docname): - d.schedule_date = cstr( add_days( obj.doc.transaction_date, cint(ltd))) - # Client Trigger functions #------------------------------------------------------------------------------------------------ diff --git a/buying/doctype/purchase_order/purchase_order.js b/buying/doctype/purchase_order/purchase_order.js index afe4741c86..ad2db91577 100644 --- a/buying/doctype/purchase_order/purchase_order.js +++ b/buying/doctype/purchase_order/purchase_order.js @@ -39,26 +39,6 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( cur_frm.add_custom_button('Unstop Purchase Order', cur_frm.cscript['Unstop Purchase Order']); }, - - onload_post_render: function(doc, dt, dn) { - var me = this; - var callback1 = function(doc, dt, dn) { - var callback2 = function(doc, dt, dn) { - if(doc.__islocal) cur_frm.cscript.get_default_schedule_date(doc); - } - me.update_item_details(doc, dt, dn, callback2); - } - - // TODO: improve this - if(this.frm.doc.__islocal) { - if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) { - this.price_list_name(callback1); - } else { - callback1(doc, dt, dn); - } - } - } - }); var new_cscript = new erpnext.buying.PurchaseOrderController({frm: cur_frm}); @@ -66,28 +46,6 @@ var new_cscript = new erpnext.buying.PurchaseOrderController({frm: cur_frm}); // for backward compatibility: combine new and previous states $.extend(cur_frm.cscript, new_cscript); -cur_frm.cscript.onload = function(doc, cdt, cdn) { - // set missing values in parent doc - set_missing_values(doc, { - fiscal_year: sys_defaults.fiscal_year, - conversion_rate: 1, - currency: sys_defaults.currency, - status: "Draft", - transaction_date: get_today(), - is_subcontracted: "No" - }); -} - -cur_frm.cscript.supplier = function(doc,dt,dn) { - if (doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }),'', doc, dt, dn, 1, function() { - cur_frm.refresh(); - }); - $(cur_frm.fields_dict.contact_section.row.wrapper).toggle(true); - } -} - cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.supplier) get_server_fields('get_supplier_address', JSON.stringify({supplier: doc.supplier, address: doc.supplier_address, contact: doc.contact_person}),'', doc, dt, dn, 1); } @@ -111,10 +69,6 @@ cur_frm.fields_dict.contact_person.on_new = function(dn) { locals['Contact'][dn].supplier_name = locals[cur_frm.doctype][cur_frm.docname].supplier_name; } -cur_frm.cscript.transaction_date = function(doc,cdt,cdn){ - if(doc.__islocal){ cur_frm.cscript.get_default_schedule_date(doc); } -} - cur_frm.fields_dict['po_details'].grid.get_field('project_name').get_query = function(doc, cdt, cdn) { return 'SELECT `tabProject`.name FROM `tabProject` \ WHERE `tabProject`.status not in ("Completed", "Cancelled") \ diff --git a/buying/doctype/purchase_order/purchase_order.py b/buying/doctype/purchase_order/purchase_order.py index 218d4bfeee..bf59389213 100644 --- a/buying/doctype/purchase_order/purchase_order.py +++ b/buying/doctype/purchase_order/purchase_order.py @@ -65,10 +65,6 @@ class DocType(BuyingController): self.validate_for_subcontracting() self.update_raw_materials_supplied("po_raw_material_details") - - def get_default_schedule_date(self): - get_obj(dt = 'Purchase Common').get_default_schedule_date(self) - def validate_fiscal_year(self): get_obj(dt = 'Purchase Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.transaction_date,'PO Date') @@ -101,7 +97,6 @@ class DocType(BuyingController): """[['Supplier Quotation', 'Purchase Order'], ['Supplier Quotation Item', 'Purchase Order Item'], ['Purchase Taxes and Charges', 'Purchase Taxes and Charges']]""") - self.get_default_schedule_date() for d in getlist(self.doclist, 'po_details'): if d.prevdoc_detail_docname and not d.schedule_date: d.schedule_date = webnotes.conn.get_value("Material Request Item", diff --git a/buying/doctype/supplier_quotation/supplier_quotation.js b/buying/doctype/supplier_quotation/supplier_quotation.js index 1e4f6cbfb7..4e5b62b4e5 100644 --- a/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/buying/doctype/supplier_quotation/supplier_quotation.js @@ -26,29 +26,11 @@ wn.require('app/buying/doctype/purchase_common/purchase_common.js'); erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({ refresh: function() { this._super(); - if (this.frm.doc.docstatus === 1) { cur_frm.add_custom_button("Make Purchase Order", cur_frm.cscript.make_purchase_order); } - }, - - onload_post_render: function(doc, dt, dn) { - var me = this; - var callback = function(doc, dt, dn) { - cur_frm.cscript.load_taxes(me.frm.doc); - } - - // TODO: improve this - if(this.frm.doc.__islocal) { - if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) { - this.price_list_name(callback); - } else { - callback(doc, dt, dn); - } - } - } - + }, }); var new_cscript = new erpnext.buying.SupplierQuotationController({frm: cur_frm}); @@ -56,19 +38,6 @@ var new_cscript = new erpnext.buying.SupplierQuotationController({frm: cur_frm}) // for backward compatibility: combine new and previous states $.extend(cur_frm.cscript, new_cscript); - -cur_frm.cscript.onload = function(doc, dt, dn) { - // set missing values in parent doc - set_missing_values(doc, { - fiscal_year: sys_defaults.fiscal_year, - conversion_rate: 1, - currency: sys_defaults.currency, - status: "Draft", - transaction_date: get_today(), - is_subcontracted: "No" - }); -} - cur_frm.cscript.make_purchase_order = function() { var new_po_name = wn.model.make_new_doc_and_get_name("Purchase Order"); $c("dt_map", { diff --git a/buying/utils.py b/buying/utils.py index 952d70a602..2c29e2c275 100644 --- a/buying/utils.py +++ b/buying/utils.py @@ -65,7 +65,10 @@ def get_item_details(args): out.purchase_ref_rate = out.discount_rate = out.purchase_rate = \ out.import_ref_rate = out.import_rate = 0.0 out.update(_get_price_list_rate(args, item_bean, meta)) - + + if args.doctype == "Material Request": + out.min_order_qty = flt(item.min_order_qty) + return out def _get_basic_details(args, item_bean): diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index 19b2a50ff9..1499e0a306 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -16,14 +16,254 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import flt - -from utilities.transaction_base import TransactionBase +from webnotes import _, msgprint +from webnotes.utils import flt, cint +from setup.utils import get_company_currency, get_price_list_currency +from utilities.transaction_base import TransactionBase, validate_conversion_rate +import json class AccountsController(TransactionBase): def validate(self): - if self.meta.get_field("grand_total"): + self.set_missing_values(for_validate=True) + + if self.meta.get_field("currency"): + self.company_currency = get_company_currency(self.doc.company) + + validate_conversion_rate(self.doc.currency, self.doc.conversion_rate, + self.meta.get_label("conversion_rate"), self.doc.company) + + # self.calculate_taxes_and_totals() self.validate_value("grand_total", ">=", 0) + self.set_total_in_words() + + def set_price_list_currency(self, buying_or_selling): + # TODO - change this, since price list now has only one currency allowed + if self.meta.get_field("price_list_name") and self.doc.price_list_name and \ + not self.doc.price_list_currency: + self.doc.fields.update(get_price_list_currency({ + "price_list_name": self.doc.price_list_name, + "use_for": buying_or_selling + })) + + def set_missing_item_details(self, get_item_details): + """set missing item values""" + for item in self.doclist.get({"parentfield": self.fname}): + if item.fields.get("item_code"): + args = item.fields.copy().update(self.doc.fields) + ret = get_item_details(args) + for fieldname, value in ret.items(): + if self.meta.get_field(fieldname, parentfield=self.fname) and \ + not item.fields.get(fieldname): + item.fields[fieldname] = value + + def set_taxes(self, tax_doctype, tax_parentfield, tax_master_field): + if not self.meta.get_field(tax_parentfield): + return + + if not self.doclist.get({"parentfield": tax_parentfield}): + if not self.doc.fields.get(tax_master_field): + # get the default tax master + self.doc.fields[tax_master_field] = \ + webnotes.conn.get_value(tax_doctype + " Master", {"is_default": 1}) + + if self.doc.fields.get(tax_master_field): + from webnotes.model import default_fields + tax_master = webnotes.bean(tax_doctype, self.doc.fields.get(tax_master_field)) + + for i, tax in enumerate(tax_master.doclist.get({"parentfield": tax_parentfield})): + for fieldname in default_fields: + tax.fields[fieldname] = None + + tax.fields.update({ + "doctype": tax_doctype, + "parentfield": tax_parentfield, + "idx": i+1 + }) + + self.doclist.append(tax) + + def calculate_taxes_and_totals(self): + self.doc.conversion_rate = flt(self.doc.conversion_rate) + + # TODO validate conversion rate + + self.item_doclist = self.doclist.get({"parentfield": self.fname}) + self.tax_doclist = self.doclist.get({"parentfield": self.other_fname}) + + self.calculate_item_values() + self.initialize_taxes() + + if hasattr(self, "determine_exclusive_rate"): + self.determine_exclusive_rate() + + self.calculate_net_total() + self.calculate_taxes() + self.calculate_totals() + self._cleanup() + + # TODO + # print format: show net_total_export instead of net_total + + def initialize_taxes(self): + for tax in self.tax_doclist: + tax.item_wise_tax_detail = {} + for fieldname in ["tax_amount", "total", + "tax_amount_for_current_item", "grand_total_for_current_item", + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]: + tax.fields[fieldname] = 0.0 + + self.validate_on_previous_row(tax) + self.validate_inclusive_tax(tax) + self.round_floats_in(tax) + + def validate_on_previous_row(self, tax): + """ + validate if a valid row id is mentioned in case of + On Previous Row Amount and On Previous Row Total + """ + if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \ + (not tax.row_id or cint(tax.row_id) >= tax.idx): + msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \ + _("Please specify a valid") + " %(row_id_label)s") % { + "idx": tax.idx, + "taxes_doctype": tax.doctype, + "row_id_label": self.meta.get_label("row_id", + parentfield=self.other_fname) + }, raise_exception=True) + + def validate_inclusive_tax(self, tax): + def _on_previous_row_error(row_range): + msgprint((_("Row") + " # %(idx)s [%(doctype)s]: " + + _("to be included in Item's rate, it is required that: ") + + " [" + _("Row") + " # %(row_range)s] " + _("also be included in Item's rate")) % { + "idx": tax.idx, + "doctype": tax.doctype, + "inclusive_label": self.meta.get_label("included_in_print_rate", + parentfield=self.other_fname), + "charge_type_label": self.meta.get_label("charge_type", + parentfield=self.other_fname), + "charge_type": tax.charge_type, + "row_range": row_range + }, raise_exception=True) + + if cint(tax.included_in_print_rate): + if tax.charge_type == "Actual": + # inclusive tax cannot be of type Actual + msgprint((_("Row") + + " # %(idx)s [%(doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" " + + "cannot be included in Item's rate") % { + "idx": tax.idx, + "doctype": tax.doctype, + "charge_type_label": self.meta.get_label("charge_type", + parentfield=self.other_fname), + "charge_type": tax.charge_type, + }, raise_exception=True) + elif tax.charge_type == "On Previous Row Amount" and \ + not cint(self.tax_doclist[tax.row_id - 1].included_in_print_rate): + # referred row should also be inclusive + _on_previous_row_error(tax.row_id) + elif tax.charge_type == "On Previous Row Total" and \ + not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:tax.row_id - 1]]): + # all rows about the reffered tax should be inclusive + _on_previous_row_error("1 - %d" % (tax.row_id,)) + + def calculate_taxes(self): + for item in self.item_doclist: + item_tax_map = self._load_item_tax_rate(item.item_tax_rate) + + for i, tax in enumerate(self.tax_doclist): + # tax_amount represents the amount of tax for the current step + current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map) + + if hasattr(self, "set_item_tax_amount"): + self.set_item_tax_amount(item, tax, current_tax_amount) + + # case when net total is 0 but there is an actual type charge + # in this case add the actual amount to tax.tax_amount + # and tax.grand_total_for_current_item for the first such iteration + if tax.charge_type=="Actual" and \ + not (current_tax_amount or self.doc.net_total or tax.tax_amount): + zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax)) + current_tax_amount += zero_net_total_adjustment + + # store tax_amount for current item as it will be used for + # charge type = 'On Previous Row Amount' + tax.tax_amount_for_current_item = current_tax_amount + + # accumulate tax amount into tax.tax_amount + tax.tax_amount += current_tax_amount + + # store tax breakup for each item + tax.item_wise_tax_detail[item.item_code] = current_tax_amount + + if tax.category: + # if just for valuation, do not add the tax amount in total + # hence, setting it as 0 for further steps + current_tax_amount = 0.0 if (tax.category == "Valuation") else current_tax_amount + + current_tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0 + + # Calculate tax.total viz. grand total till that step + # note: grand_total_for_current_item contains the contribution of + # item's amount, previously applied tax and the current tax on that item + if i==0: + tax.grand_total_for_current_item = flt(item.amount + + current_tax_amount, self.precision("total", tax)) + + else: + tax.grand_total_for_current_item = \ + flt(self.tax_doclist[i-1].grand_total_for_current_item + + current_tax_amount, self.precision("total", tax)) + + # in tax.total, accumulate grand total of each item + tax.total += tax.grand_total_for_current_item + + def get_current_tax_amount(self, item, tax, item_tax_map): + tax_rate = self._get_tax_rate(tax, item_tax_map) + current_tax_amount = 0.0 + + if tax.charge_type == "Actual": + # distribute the tax amount proportionally to each item row + actual = flt(tax.rate, self.precision("tax_amount", tax)) + current_tax_amount = (self.doc.net_total + and ((item.amount / self.doc.net_total) * actual) + or 0) + elif tax.charge_type == "On Net Total": + current_tax_amount = (tax_rate / 100.0) * item.amount + elif tax.charge_type == "On Previous Row Amount": + current_tax_amount = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item + elif tax.charge_type == "On Previous Row Total": + current_tax_amount = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item + + return flt(current_tax_amount, self.precision("tax_amount", tax)) + + def _load_item_tax_rate(self, item_tax_rate): + return json.loads(item_tax_rate) if item_tax_rate else {} + + def _get_tax_rate(self, tax, item_tax_map): + if item_tax_map.has_key(tax.account_head): + return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax)) + else: + return tax.rate + + def _cleanup(self): + for tax in self.tax_doclist: + for fieldname in ("grand_total_for_current_item", + "tax_amount_for_current_item", + "tax_fraction_for_current_item", + "grand_total_fraction_for_current_item"): + if fieldname in tax.fields: + del tax.fields[fieldname] + + tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail) + + def _set_in_company_currency(self, item, print_field, base_field): + """set values in base currency""" + item.fields[base_field] = flt((flt(item.fields[print_field], + self.precision(print_field, item)) * self.doc.conversion_rate), + self.precision(base_field, item)) def get_gl_dict(self, args, cancel=None): """this method populates the common properties of a gl entry record""" diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 1fc411fad4..8313b5409d 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -17,34 +17,45 @@ from __future__ import unicode_literals import webnotes from webnotes import _, msgprint -from webnotes.utils import flt, cint -import json +from webnotes.utils import flt from buying.utils import get_item_details from setup.utils import get_company_currency -from utilities.transaction_base import validate_conversion_rate from controllers.stock_controller import StockController class WrongWarehouseCompany(Exception): pass class BuyingController(StockController): + def onload_post_render(self): + self.set_price_list_currency("buying") + + # contact, address, item details + self.set_missing_values() + + self.set_taxes("Purchase Taxes and Charges", "purchase_tax_details", "purchase_other_charges") + def validate(self): super(BuyingController, self).validate() self.validate_stock_or_nonstock_items() self.validate_warehouse_belongs_to_company() - if self.meta.get_field("currency"): - self.company_currency = get_company_currency(self.doc.company) - validate_conversion_rate(self.doc.currency, self.doc.conversion_rate, - self.meta.get_label("conversion_rate"), self.doc.company) - - # IMPORTANT: enable this only when client side code is similar to this one - # self.calculate_taxes_and_totals() + def set_missing_values(self, for_validate=False): + # set contact and address details for supplier, if they are not mentioned + if self.doc.supplier and not (self.doc.contact_person and self.doc.supplier_address): + for fieldname, val in self.get_default_address_and_contact("supplier").items(): + if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): + self.doc.fields[fieldname] = val + + self.set_missing_item_details(get_item_details) + + def set_supplier_defaults(self): + self.get_default_supplier_address() - # set total in words - self.set_total_in_words() - + def get_purchase_tax_details(self): + self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details") + self.set_taxes() + def validate_warehouse_belongs_to_company(self): for warehouse, company in webnotes.conn.get_values("Warehouse", self.doclist.get_distinct_values("warehouse"), "company").items(): @@ -70,24 +81,6 @@ class BuyingController(StockController): webnotes.msgprint(_("""Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"""), raise_exception=1) - def update_item_details(self): - for item in self.doclist.get({"parentfield": self.fname}): - ret = get_item_details({ - "doctype": self.doc.doctype, - "docname": self.doc.name, - "item_code": item.item_code, - "warehouse": item.warehouse, - "supplier": self.doc.supplier, - "transaction_date": self.doc.posting_date, - "conversion_rate": self.doc.conversion_rate, - "price_list_name": self.doc.price_list_name, - "price_list_currency": self.doc.price_list_currency, - "plc_conversion_rate": self.doc.plc_conversion_rate - }) - for r in ret: - if not item.fields.get(r): - item.fields[r] = ret[r] - def set_total_in_words(self): from webnotes.utils import money_in_words company_currency = get_company_currency(self.doc.company) @@ -98,26 +91,11 @@ class BuyingController(StockController): self.doc.currency) def calculate_taxes_and_totals(self): - self.doc.conversion_rate = flt(self.doc.conversion_rate) - self.item_doclist = self.doclist.get({"parentfield": self.fname}) - self.tax_doclist = self.doclist.get({"parentfield": "purchase_tax_details"}) - - self.calculate_item_values() - self.initialize_taxes() - self.calculate_net_total() - self.calculate_taxes() - self.calculate_totals() + self.other_fname = "purchase_tax_details" + super(BuyingController, self).calculate_taxes_and_totals() self.calculate_outstanding_amount() - self._cleanup() - def calculate_item_values(self): - def _set_base(item, print_field, base_field): - """set values in base currency""" - item.fields[base_field] = flt((flt(item.fields[print_field], - self.precision(print_field, item)) * self.doc.conversion_rate), - self.precision(base_field, item)) - # hack! - cleaned up in _cleanup() if self.doc.doctype != "Purchase Invoice": df = self.meta.get_field("purchase_rate", parentfield=self.fname) @@ -130,119 +108,36 @@ class BuyingController(StockController): self.round_floats_in(item) - if item.discount_rate == 100: - item.import_ref_rate = item.import_ref_rate or item.import_rate - item.import_rate = 0 - else: - if item.import_ref_rate: - item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)), - self.precision("import_rate", item)) - else: - # assume that print rate and discount_rate are specified - item.import_ref_rate = flt(item.import_rate / (1.0 - (item.discount_rate / 100.0)), - self.precision("import_ref_rate", item)) + if item.discount_rate == 100.0: + item.import_rate = 0.0 + elif item.import_ref_rate: + item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)), + self.precision("import_rate", item)) item.import_amount = flt(item.import_rate * item.qty, self.precision("import_amount", item)) + item.item_tax_amount = 0.0; - _set_base(item, "import_ref_rate", "purchase_ref_rate") - _set_base(item, "import_rate", "rate") - _set_base(item, "import_amount", "amount") + self._set_in_company_currency(item, "import_ref_rate", "purchase_ref_rate") + self._set_in_company_currency(item, "import_rate", "rate") + self._set_in_company_currency(item, "import_amount", "amount") - def initialize_taxes(self): - for tax in self.tax_doclist: - # initialize totals to 0 - tax.tax_amount = tax.total = 0.0 - - # temporary fields - tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0 - - tax.item_wise_tax_detail = {} - - self.validate_on_previous_row(tax) - - self.round_floats_in(tax) - def calculate_net_total(self): - self.doc.net_total = 0 - self.doc.net_total_import = 0 + self.doc.net_total = self.doc.net_total_import = 0.0 for item in self.item_doclist: self.doc.net_total += item.amount self.doc.net_total_import += item.import_amount - self.doc.net_total = flt(self.doc.net_total, self.precision("net_total")) - self.doc.net_total_import = flt(self.doc.net_total_import, - self.precision("net_total_import")) - - def calculate_taxes(self): - for item in self.item_doclist: - item_tax_map = self._load_item_tax_rate(item.item_tax_rate) - item.item_tax_amount = 0 - - for i, tax in enumerate(self.tax_doclist): - # tax_amount represents the amount of tax for the current step - current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map) - - self.set_item_tax_amount(item, tax, current_tax_amount) - - # case when net total is 0 but there is an actual type charge - # in this case add the actual amount to tax.tax_amount - # and tax.grand_total_for_current_item for the first such iteration - if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ - tax.charge_type=="Actual": - zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax)) - current_tax_amount += zero_net_total_adjustment - - # store tax_amount for current item as it will be used for - # charge type = 'On Previous Row Amount' - tax.tax_amount_for_current_item = current_tax_amount - - # accumulate tax amount into tax.tax_amount - tax.tax_amount += tax.tax_amount_for_current_item - - if tax.category == "Valuation": - # if just for valuation, do not add the tax amount in total - # hence, setting it as 0 for further steps - current_tax_amount = 0 - else: - current_tax_amount *= tax.add_deduct_tax == "Deduct" and -1.0 or 1.0 - - # Calculate tax.total viz. grand total till that step - # note: grand_total_for_current_item contains the contribution of - # item's amount, previously applied tax and the current tax on that item - if i==0: - tax.grand_total_for_current_item = flt(item.amount + - current_tax_amount, self.precision("total", tax)) - - else: - tax.grand_total_for_current_item = \ - flt(self.tax_doclist[i-1].grand_total_for_current_item + - current_tax_amount, self.precision("total", tax)) - - # in tax.total, accumulate grand total of each item - tax.total += tax.grand_total_for_current_item - - # store tax_breakup for each item - # DOUBT: should valuation type amount also be stored? - tax.item_wise_tax_detail[item.item_code] = current_tax_amount + self.round_floats_in(self.doc, ["net_total", "net_total_import"]) def calculate_totals(self): - if self.tax_doclist: - self.doc.grand_total = flt(self.tax_doclist[-1].total, - self.precision("grand_total")) - self.doc.grand_total_import = flt( - self.doc.grand_total / self.doc.conversion_rate, - self.precision("grand_total_import")) - else: - self.doc.grand_total = flt(self.doc.net_total, - self.precision("grand_total")) - self.doc.grand_total_import = flt( - self.doc.grand_total / self.doc.conversion_rate, - self.precision("grand_total_import")) + self.doc.grand_total = flt(self.tax_doclist and \ + self.tax_doclist[-1].total or self.doc.net_total, self.precision("grand_total")) + self.doc.grand_total_import = flt(self.doc.grand_total / self.doc.conversion_rate, + self.precision("grand_total_import")) - self.doc.total_tax = \ - flt(self.doc.grand_total - self.doc.net_total, + self.doc.total_tax = flt(self.doc.grand_total - self.doc.net_total, self.precision("total_tax")) if self.meta.get_field("rounded_total"): @@ -261,68 +156,18 @@ class BuyingController(StockController): self.precision("outstanding_amount")) def _cleanup(self): - for tax in self.tax_doclist: - del tax.fields["grand_total_for_current_item"] - del tax.fields["tax_amount_for_current_item"] - tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail) - - # except in purchase invoice, rate field is purchase_rate - if self.doc.doctype != "Purchase Invoice": - for item in self.item_doclist: - item.purchase_rate = item.rate - del item.fields["rate"] - + super(BuyingController, self)._cleanup() + + # except in purchase invoice, rate field is purchase_rate # reset fieldname of rate if self.doc.doctype != "Purchase Invoice": df = self.meta.get_field("rate", parentfield=self.fname) df.fieldname = "purchase_rate" - - def validate_on_previous_row(self, tax): - """ - validate if a valid row id is mentioned in case of - On Previous Row Amount and On Previous Row Total - """ - if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \ - (not tax.row_id or cint(tax.row_id) >= tax.idx): - msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \ - _("Please specify a valid") + " %(row_id_label)s") % { - "idx": tax.idx, - "taxes_doctype": tax.parenttype, - "row_id_label": self.meta.get_label("row_id", - parentfield="purchase_tax_details") - }, raise_exception=True) - - def _load_item_tax_rate(self, item_tax_rate): - if not item_tax_rate: - return {} - return json.loads(item_tax_rate) - - def get_current_tax_amount(self, item, tax, item_tax_map): - tax_rate = self._get_tax_rate(tax, item_tax_map) - - if tax.charge_type == "Actual": - # distribute the tax amount proportionally to each item row - actual = flt(tax.rate, self.precision("tax_amount", tax)) - current_tax_amount = (self.doc.net_total - and ((item.amount / self.doc.net_total) * actual) - or 0) - elif tax.charge_type == "On Net Total": - current_tax_amount = (tax_rate / 100.0) * item.amount - elif tax.charge_type == "On Previous Row Amount": - current_tax_amount = (tax_rate / 100.0) * \ - self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item - elif tax.charge_type == "On Previous Row Total": - current_tax_amount = (tax_rate / 100.0) * \ - self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item - - return flt(current_tax_amount, self.precision("tax_amount", tax)) - - def _get_tax_rate(self, tax, item_tax_map): - if item_tax_map.has_key(tax.account_head): - return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax)) - else: - return tax.rate + for item in self.item_doclist: + item.purchase_rate = item.rate + del item.fields["rate"] + def set_item_tax_amount(self, item, tax, current_tax_amount): """ item_tax_amount is the total tax amount applied on that item @@ -330,10 +175,8 @@ class BuyingController(StockController): TODO: rename item_tax_amount to valuation_tax_amount """ - if tax.category in ["Valuation", "Valuation and Total"] and \ - item.item_code in self.stock_items: - item.item_tax_amount += flt(current_tax_amount, - self.precision("item_tax_amount", item)) + if tax.category in ["Valuation", "Valuation and Total"]: + item.item_tax_amount += flt(current_tax_amount, self.precision("item_tax_amount", item)) # update valuation rate def update_valuation_rate(self, parentfield): diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 01c8c61702..de90fa7a13 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -18,34 +18,20 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import cint, flt, comma_or from setup.utils import get_company_currency +from selling.utils import get_item_details from webnotes import msgprint, _ -import json from controllers.stock_controller import StockController class SellingController(StockController): def onload_post_render(self): - self.set_price_list_currency() + self.set_price_list_currency("selling") # contact, address, item details and pos details (if applicable) self.set_missing_values() - if self.meta.get_field("other_charges"): - self.set_taxes() + self.set_taxes("Sales Taxes and Charges", "other_charges", "charge") - def validate(self): - super(SellingController, self).validate() - # self.calculate_taxes_and_totals() - self.set_total_in_words() - self.set_missing_values(for_validate=True) - - def set_price_list_currency(self): - if self.doc.price_list_name and not self.doc.price_list_currency: - # TODO - change this, since price list now has only one currency allowed - from setup.utils import get_price_list_currency - self.doc.fields.update(get_price_list_currency( - {"price_list_name": self.doc.price_list_name, "use_for": "selling"})) - def set_missing_values(self, for_validate=False): # set contact and address details for customer, if they are not mentioned if self.doc.customer and not (self.doc.contact_person and self.doc.customer_address): @@ -53,42 +39,11 @@ class SellingController(StockController): if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): self.doc.fields[fieldname] = val - # set missing item values - from selling.utils import get_item_details - for item in self.doclist.get({"parentfield": "entries"}): - if item.fields.get("item_code"): - args = item.fields.copy().update(self.doc.fields) - ret = get_item_details(args) - for fieldname, value in ret.items(): - if self.meta.get_field(fieldname, parentfield="entries") and \ - not item.fields.get(fieldname): - item.fields[fieldname] = value + self.set_missing_item_details(get_item_details) - def set_taxes(self): - if not self.doclist.get({"parentfield": "other_charges"}): - if not self.doc.charge: - # get the default tax master - self.doc.charge = webnotes.conn.get_value("Sales Taxes and Charges Master", - {"is_default": 1}) - - if self.doc.charge: - from webnotes.model import default_fields - tax_master = webnotes.bean("Sales Taxes and Charges Master", self.doc.charge) - for i, tax in enumerate(tax_master.doclist.get({"parentfield": "other_charges"})): - for fieldname in default_fields: - tax.fields[fieldname] = None - - tax.fields.update({ - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "idx": i+1 - }) - - self.doclist.append(tax) - def get_other_charges(self): self.doclist = self.doc.clear_table(self.doclist, "other_charges") - self.set_taxes() + self.set_taxes("Sales Taxes and Charges", "other_charges", "charge") def set_customer_defaults(self): self.get_default_customer_address() @@ -140,24 +95,14 @@ class SellingController(StockController): raise_exception=1) def calculate_taxes_and_totals(self): - self.doc.conversion_rate = flt(self.doc.conversion_rate) - self.item_doclist = self.doclist.get({"parentfield": self.fname}) - self.tax_doclist = self.doclist.get({"parentfield": "other_charges"}) + self.other_fname = "other_charges" + + super(SellingController, self).calculate_taxes_and_totals() - self.calculate_item_values() - self.initialize_taxes() - self.determine_exclusive_rate() - self.calculate_net_total() - self.calculate_taxes() - self.calculate_totals() self.calculate_commission() self.calculate_contribution() # self.calculate_outstanding_amount() - self._cleanup() - - # TODO - # print format: show net_total_export instead of net_total - + def determine_exclusive_rate(self): if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)): # no inclusive tax @@ -215,45 +160,21 @@ class SellingController(StockController): return current_tax_fraction def calculate_item_values(self): - def _set_base(item, print_field, base_field): - """set values in base currency""" - item.fields[base_field] = flt((flt(item.fields[print_field], - self.precision(print_field, item)) * self.doc.conversion_rate), - self.precision(base_field, item)) - for item in self.item_doclist: self.round_floats_in(item) if item.adj_rate == 100: - item.ref_rate = item.ref_rate or item.export_rate item.export_rate = 0 - else: - if item.ref_rate: - item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)), - self.precision("export_rate", item)) - else: - # assume that print rate and discount are specified - item.ref_rate = flt(item.export_rate / (1.0 - (item.adj_rate / 100.0)), - self.precision("ref_rate", item)) + elif item.ref_rate: + item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)), + self.precision("export_rate", item)) item.export_amount = flt(item.export_rate * item.qty, self.precision("export_amount", item)) - _set_base(item, "ref_rate", "base_ref_rate") - _set_base(item, "export_rate", "basic_rate") - _set_base(item, "export_amount", "amount") - - def initialize_taxes(self): - for tax in self.tax_doclist: - tax.tax_amount = tax.total = 0.0 - tax.item_wise_tax_detail = {} - - # temporary fields - tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0 - - self.validate_on_previous_row(tax) - self.validate_inclusive_tax(tax) - self.round_floats_in(tax) + self._set_in_company_currency(item, "ref_rate", "base_ref_rate") + self._set_in_company_currency(item, "export_rate", "basic_rate") + self._set_in_company_currency(item, "export_amount", "amount") def calculate_net_total(self): self.doc.net_total = self.doc.net_total_export = 0.0 @@ -263,47 +184,6 @@ class SellingController(StockController): self.doc.net_total_export += item.export_amount self.round_floats_in(self.doc, ["net_total", "net_total_export"]) - - def calculate_taxes(self): - for item in self.item_doclist: - item_tax_map = self._load_item_tax_rate(item.item_tax_rate) - - for i, tax in enumerate(self.tax_doclist): - # tax_amount represents the amount of tax for the current step - current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map) - - # case when net total is 0 but there is an actual type charge - # in this case add the actual amount to tax.tax_amount - # and tax.grand_total_for_current_item for the first such iteration - if tax.charge_type=="Actual" and \ - not (current_tax_amount or self.doc.net_total or tax.tax_amount): - zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax)) - current_tax_amount += zero_net_total_adjustment - - # store tax_amount for current item as it will be used for - # charge type = 'On Previous Row Amount' - tax.tax_amount_for_current_item = current_tax_amount - - # accumulate tax amount into tax.tax_amount - tax.tax_amount += current_tax_amount - - # Calculate tax.total viz. grand total till that step - # note: grand_total_for_current_item contains the contribution of - # item's amount, previously applied tax and the current tax on that item - if i==0: - tax.grand_total_for_current_item = flt(item.amount + - current_tax_amount, self.precision("total", tax)) - - else: - tax.grand_total_for_current_item = \ - flt(self.tax_doclist[i-1].grand_total_for_current_item + - current_tax_amount, self.precision("total", tax)) - - # in tax.total, accumulate grand total of each item - tax.total += tax.grand_total_for_current_item - - # store tax breakup for each item - tax.item_wise_tax_detail[item.item_code] = current_tax_amount def calculate_totals(self): self.doc.grand_total = flt(self.tax_doclist and \ @@ -344,98 +224,6 @@ class SellingController(StockController): msgprint(_("Total") + " " + _(self.meta.get_label("allocated_percentage", parentfield="sales_team")) + " " + _("should be 100%"), raise_exception=True) - - def get_current_tax_amount(self, item, tax, item_tax_map): - tax_rate = self._get_tax_rate(tax, item_tax_map) - current_tax_amount = 0.0 - - if tax.charge_type == "Actual": - # distribute the tax amount proportionally to each item row - actual = flt(tax.rate, self.precision("tax_amount", tax)) - current_tax_amount = (self.doc.net_total - and ((item.amount / self.doc.net_total) * actual) - or 0) - elif tax.charge_type == "On Net Total": - current_tax_amount = (tax_rate / 100.0) * item.amount - elif tax.charge_type == "On Previous Row Amount": - current_tax_amount = (tax_rate / 100.0) * \ - self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item - elif tax.charge_type == "On Previous Row Total": - current_tax_amount = (tax_rate / 100.0) * \ - self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item - - return flt(current_tax_amount, self.precision("tax_amount", tax)) - - def validate_on_previous_row(self, tax): - """ - validate if a valid row id is mentioned in case of - On Previous Row Amount and On Previous Row Total - """ - if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \ - (not tax.row_id or cint(tax.row_id) >= tax.idx): - msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \ - _("Please specify a valid") + " %(row_id_label)s") % { - "idx": tax.idx, - "taxes_doctype": tax.doctype, - "row_id_label": self.meta.get_label("row_id", - parentfield="other_charges") - }, raise_exception=True) - - def validate_inclusive_tax(self, tax): - def _on_previous_row_error(row_range): - msgprint((_("Row") + " # %(idx)s [%(doctype)s]: " + - _("to be included in Item's rate, it is required that: ") + - " [" + _("Row") + " # %(row_range)s] " + _("also be included in Item's rate")) % { - "idx": tax.idx, - "doctype": tax.doctype, - "inclusive_label": self.meta.get_label("included_in_print_rate", - parentfield="other_charges"), - "charge_type_label": self.meta.get_label("charge_type", - parentfield="other_charges"), - "charge_type": tax.charge_type, - "row_range": row_range - }, raise_exception=True) - - if cint(tax.included_in_print_rate): - if tax.charge_type == "Actual": - # inclusive tax cannot be of type Actual - msgprint((_("Row") - + " # %(idx)s [%(doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" " - + "cannot be included in Item's rate") % { - "idx": tax.idx, - "doctype": tax.doctype, - "charge_type_label": self.meta.get_label("charge_type", - parentfield="other_charges"), - "charge_type": tax.charge_type, - }, raise_exception=True) - elif tax.charge_type == "On Previous Row Amount" and \ - not cint(self.tax_doclist[tax.row_id - 1].included_in_print_rate): - # referred row should also be inclusive - _on_previous_row_error(tax.row_id) - elif tax.charge_type == "On Previous Row Total" and \ - not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:tax.row_id - 1]]): - # all rows about the reffered tax should be inclusive - _on_previous_row_error("1 - %d" % (tax.row_id,)) - - def _load_item_tax_rate(self, item_tax_rate): - return json.loads(item_tax_rate) if item_tax_rate else {} - - def _get_tax_rate(self, tax, item_tax_map): - if item_tax_map.has_key(tax.account_head): - return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax)) - else: - return tax.rate - - def _cleanup(self): - for tax in self.tax_doclist: - for fieldname in ("grand_total_for_current_item", - "tax_amount_for_current_item", - "tax_fraction_for_current_item", - "grand_total_fraction_for_current_item"): - if fieldname in tax.fields: - del tax.fields[fieldname] - - tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail) def validate_order_type(self): valid_types = ["Sales", "Maintenance"] diff --git a/public/js/transaction.js b/public/js/transaction.js new file mode 100644 index 0000000000..34e10f6f56 --- /dev/null +++ b/public/js/transaction.js @@ -0,0 +1,443 @@ +// 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 . + +wn.provide("erpnext"); + +erpnext.TransactionController = wn.ui.form.Controller.extend({ + onload: function() { + if(this.frm.doc.__islocal) { + var me = this, + today = get_today(), + currency = wn.defaults.get_default("currency"); + + $.each({ + "posting_date": today, + "due_date": today, + "transaction_date": today, + "currency": currency, + "price_list_currency": currency, + "status": "Draft", + "fiscal_year": wn.defaults.get_default("fiscal_year"), + "is_subcontracted": "No", + "conversion_rate": 1.0, + "plc_conversion_rate": 1.0 + }, function(fieldname, value) { + if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) + me.frm.set_value(fieldname, value); + }); + } + }, + + refresh: function() { + this.frm.clear_custom_buttons(); + erpnext.hide_naming_series(); + this.show_item_wise_taxes(); + + if(this.frm.fields_dict.currency) + this.currency(); + }, + + onload_post_render: function() { + if(this.frm.doc.__islocal && this.frm.doc.company) { + var me = this; + this.frm.call({ + doc: this.frm.doc, + method: "onload_post_render", + freeze: true, + callback: function(r) { + // remove this call when using client side mapper + me.set_default_values(); + me.frm.refresh(); + } + }); + } + }, + + company: function() { + if(this.frm.doc.company) { + var me = this; + var company_currency = this.get_company_currency(); + $.each(["currency", "price_list_currency"], function(i, fieldname) { + if(!me.doc[fieldname]) { + me.frm.set_value(fieldname, company_currency); + } + }); + this.price_list_currency(); + } + }, + + get_company_currency: function() { + return erpnext.get_currency(this.frm.doc.company); + }, + + currency: function() { + this.price_list_currency(); + }, + + price_list_name: function(use_for) { + var me = this; + if(this.frm.doc.price_list_name) { + this.frm.call({ + method: "setup.utils.get_price_list_currency", + args: {args: { + price_list_name: this.frm.doc.price_list_name, + use_for: use_for + }}, + callback: function(r) { + if(!r.exc) { + me.price_list_currency(); + } + } + }); + } + }, + + price_list_currency: function() { + // What TODO? should we make price list system non-mandatory? + this.frm.toggle_reqd("plc_conversion_rate", + !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); + + if(this.frm.doc.price_list_currency === this.get_company_currency()) { + this.frm.set_value("plc_conversion_rate", 1.0); + } else if(this.frm.doc.price_list_currency === this.frm.doc.currency) { + this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate); + } + this.set_dynamic_labels(); + }, + + plc_conversion_rate: function() { + this.price_list_currency(); + }, + + conversion_rate: function() { + this.price_list_currency(); + this.calculate_taxes_and_totals(); + }, + + qty: function(doc, cdt, cdn) { + this.calculate_taxes_and_totals(); + }, + + included_in_print_rate: function(doc, cdt, cdn) { + var tax = wn.model.get_doc(cdt, cdn); + try { + this.validate_on_previous_row(tax); + this.validate_inclusive_tax(tax); + this.calculate_taxes_and_totals(); + } catch(e) { + tax.included_in_print_rate = 0; + refresh_field("included_in_print_rate", tax.name, tax.parentfield); + throw e; + } + }, + + validate_on_previous_row: function(tax) { + // validate if a valid row id is mentioned in case of + // On Previous Row Amount and On Previous Row Total + if((["On Previous Row Amount", "On Previous Row Total"].indexOf(tax.charge_type) != -1) && + (!tax.row_id || cint(tax.row_id) >= tax.idx)) { + var msg = repl(wn._("Row") + " # %(idx)s [%(doctype)s]: " + + wn._("Please specify a valid") + " %(row_id_label)s", { + idx: tax.idx, + doctype: tax.doctype, + row_id_label: wn.meta.get_label(tax.doctype, "row_id", tax.name) + }); + msgprint(msg); + throw msg; + } + }, + + validate_inclusive_tax: function(tax) { + if(!this.frm.tax_doclist) this.frm.tax_doclist = this.get_tax_doclist(); + + var actual_type_error = function() { + var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " + + "%(charge_type_label)s = \"%(charge_type)s\" " + + wn._("cannot be included in Item's rate"), { + idx: tax.idx, + doctype: tax.doctype, + charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name), + charge_type: tax.charge_type + }); + msgprint(msg); + throw msg; + }; + + var on_previous_row_error = function(row_range) { + var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " + + wn._("to be included in Item's rate, it is required that: ") + + " [" + wn._("Row") + " # %(row_range)s] " + wn._("also be included in Item's rate"), { + idx: tax.idx, + doctype: tax.doctype, + charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name), + charge_type: tax.charge_type, + inclusive_label: wn.meta.get_label(tax.doctype, "included_in_print_rate", tax.name), + row_range: row_range, + }); + + msgprint(msg); + throw msg; + }; + + if(cint(tax.included_in_print_rate)) { + if(tax.charge_type == "Actual") { + // inclusive tax cannot be of type Actual + actual_type_error(); + } else if(tax.charge_type == "On Previous Row Amount" && + !cint(this.frm.tax_doclist[tax.row_id - 1].included_in_print_rate)) { + // referred row should also be an inclusive tax + on_previous_row_error(tax.row_id); + } else if(tax.charge_type == "On Previous Row Total") { + var taxes_not_included = $.map(this.frm.tax_doclist.slice(0, tax.row_id), + function(t) { return cint(t.included_in_print_rate) ? null : t; }); + if(taxes_not_included.length > 0) { + // all rows above this tax should be inclusive + on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id); + } + } + } + }, + + _load_item_tax_rate: function(item_tax_rate) { + return item_tax_rate ? JSON.parse(item_tax_rate) : {}; + }, + + _get_tax_rate: function(tax, item_tax_map) { + return (keys(item_tax_map).indexOf(tax.account_head) != -1) ? + flt(item_tax_map[tax.account_head], precision("rate", tax)) : + tax.rate; + }, + + get_item_wise_taxes_html: function() { + var item_tax = {}; + var tax_accounts = []; + var company_currency = this.get_company_currency(); + + $.each(this.get_tax_doclist(), function(i, tax) { + var tax_amount_precision = precision("tax_amount", tax); + $.each(JSON.parse(tax.item_wise_tax_detail || '{}'), + function(item_code, tax_amount) { + if(!item_tax[item_code]) item_tax[item_code] = {}; + item_tax[item_code][tax.account_head] = flt(tax_amount, tax_amount_precision); + }); + tax_accounts.push(tax.account_head); + }); + + var headings = $.map([wn._("Item Name")].concat(tax_accounts), + function(head) { return '' + (head || "") + "" }).join("\n"); + + var rows = $.map(this.get_item_doclist(), function(item) { + var item_tax_record = item_tax[item.item_code || item.item_name]; + return repl("%(item_name)s%(taxes)s", { + item_name: item.item_name, + taxes: $.map(tax_accounts, function(head) { + return "" + format_currency(item_tax_record[head], company_currency) + "" + }).join("\n") + }); + }).join("\n"); + + return '
\ + ' + headings + ' \ + ' + rows + ' \ +
'; + }, + + set_default_values: function() { + $.each(wn.model.get_doclist(this.frm.doctype, this.frm.docname), function(i, doc) { + var updated = wn.model.set_default_values(doc); + if(doc.parentfield) { + refresh_field(doc.parentfield); + } else { + refresh_field(updated); + } + }); + }, + + validate_company_and_party: function(party_field) { + var valid = true; + $.each(["company", party_field], function(i, fieldname) { + if(!me.frm.doc[fieldname]) { + valid = false; + msgprint(wn._("Please specify") + ": " + + wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + ". " + wn._("It is needed to fetch Item Details.")); + } + }); + return valid; + }, + + get_item_doclist: function() { + return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: this.fname}); + }, + + get_tax_doclist: function() { + return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: this.other_fname}); + }, + + validate_conversion_rate: function() { + this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); + var conversion_rate_label = wn.meta.get_label(this.frm.doc.doctype, "conversion_rate", + this.frm.doc.name); + + if(this.frm.doc.conversion_rate == 0) { + wn.throw(wn._(conversion_rate_label) + " " + wn._("cannot be 0")); + } + + var company_currency = this.get_company_currency(); + var valid_conversion_rate = conversion_rate ? + ((this.frm.doc.currency == company_currency && this.frm.doc.conversion_rate == 1.0) || + (this.frm.doc.currency != company_currency && this.frm.doc.conversion_rate != 1.0)) : + false; + + if(!valid_conversion_rate) { + wn.throw(wn._("Please enter valid") + " " + wn._(conversion_rate_label) + + " 1 " + this.frm.doc.currency + " = [?] " + company_currency); + } + }, + + calculate_taxes_and_totals: function() { + this.validate_conversion_rate(); + this.frm.item_doclist = this.get_item_doclist(); + this.frm.tax_doclist = this.get_tax_doclist(); + + this.calculate_item_values(); + this.initialize_taxes(); + this.determine_exclusive_rate && this.determine_exclusive_rate(); + this.calculate_net_total(); + this.calculate_taxes(); + this.calculate_totals(); + this._cleanup(); + this.show_item_wise_taxes(); + }, + + initialize_taxes: function() { + var me = this; + $.each(this.frm.tax_doclist, function(i, tax) { + tax.item_wise_tax_detail = {}; + $.each(["tax_amount", "total", + "tax_amount_for_current_item", "grand_total_for_current_item", + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"], + function(i, fieldname) { tax[fieldname] = 0.0 }); + + me.validate_on_previous_row(tax); + me.validate_inclusive_tax(tax); + wn.model.round_floats_in(tax); + }); + }, + + calculate_taxes: function() { + var me = this; + + $.each(this.frm.item_doclist, function(n, item) { + var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); + + $.each(me.frm.tax_doclist, function(i, tax) { + // tax_amount represents the amount of tax for the current step + var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map); + + me.set_item_tax_amount && me.set_item_tax_amount(item, tax, current_tax_amount); + + // case when net total is 0 but there is an actual type charge + // in this case add the actual amount to tax.tax_amount + // and tax.grand_total_for_current_item for the first such iteration + if(tax.charge_type == "Actual" && + !(current_tax_amount || me.frm.doc.net_total || tax.tax_amount)) { + var zero_net_total_adjustment = flt(tax.rate, precision("tax_amount", tax)); + current_tax_amount += zero_net_total_adjustment; + } + + // store tax_amount for current item as it will be used for + // charge type = 'On Previous Row Amount' + tax.tax_amount_for_current_item = current_tax_amount; + + // accumulate tax amount into tax.tax_amount + tax.tax_amount += current_tax_amount; + + // store tax breakup for each item + tax.item_wise_tax_detail[item.item_code || item.item_name] = current_tax_amount; + + // for buying + if(tax.category) { + // if just for valuation, do not add the tax amount in total + // hence, setting it as 0 for further steps + current_tax_amount = (tax.category == "Valuation") ? 0.0 : current_tax_amount; + + current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; + } + + // Calculate tax.total viz. grand total till that step + // note: grand_total_for_current_item contains the contribution of + // item's amount, previously applied tax and the current tax on that item + if(i==0) { + tax.grand_total_for_current_item = flt(item.amount + current_tax_amount, + precision("total", tax)); + } else { + tax.grand_total_for_current_item = + flt(me.frm.tax_doclist[i-1].grand_total_for_current_item + current_tax_amount, + precision("total", tax)); + } + + // in tax.total, accumulate grand total for each item + tax.total += tax.grand_total_for_current_item; + }); + }); + }, + + get_current_tax_amount: function(item, tax, item_tax_map) { + var tax_rate = this._get_tax_rate(tax, item_tax_map); + var current_tax_amount = 0.0; + + if(tax.charge_type == "Actual") { + // distribute the tax amount proportionally to each item row + var actual = flt(tax.rate, precision("tax_amount", tax)); + current_tax_amount = this.frm.doc.net_total ? + ((item.amount / this.frm.doc.net_total) * actual) : + 0.0; + + } else if(tax.charge_type == "On Net Total") { + current_tax_amount = (tax_rate / 100.0) * item.amount; + + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_amount = (tax_rate / 100.0) * + this.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item; + + } else if(tax.charge_type == "On Previous Row Total") { + current_tax_amount = (tax_rate / 100.0) * + this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; + + } + + return flt(current_tax_amount, precision("tax_amount", tax)); + }, + + _cleanup: function() { + $.each(this.frm.tax_doclist, function(i, tax) { + $.each(["tax_amount_for_current_item", "grand_total_for_current_item", + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"], + function(i, fieldname) { delete tax[fieldname]; }); + + tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail); + }); + }, + + _set_in_company_currency: function(item, print_field, base_field) { + // set values in base currency + item[base_field] = flt(item[print_field] * this.frm.doc.conversion_rate, + precision(base_field, item)); + }, +}); \ No newline at end of file diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js index 078baf1997..a7c542196e 100644 --- a/selling/doctype/quotation/quotation.js +++ b/selling/doctype/quotation/quotation.js @@ -53,14 +53,6 @@ cur_frm.cscript.onload = function(doc, cdt, cdn) { } } -cur_frm.cscript.onload_post_render = function(doc, dt, dn) { - var callback = function(doc, dt, dn) { - // defined in sales_common.js - cur_frm.cscript.update_item_details(doc, dt, dn); - } - cur_frm.cscript.hide_price_list_currency(doc, dt, dn, callback); -} - // hide - unhide fields based on lead or customer.. // ======================================================================================================================= cur_frm.cscript.lead_cust_show = function(doc,cdt,cdn){ diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js index fd3f5c58df..4d97034210 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -22,61 +22,19 @@ // cur_frm.cscript.sales_team_fname - Sales Team fieldname wn.provide("erpnext.selling"); +wn.require("app/js/transaction.js"); -erpnext.selling.SellingController = wn.ui.form.Controller.extend({ +erpnext.selling.SellingController = erpnext.TransactionController.extend({ setup: function() { this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate"); }, - // events when rendering form // 1 onload: function() { - var me = this; + this._super(); this.toggle_rounded_total(); - if(this.frm.doc.__islocal) { - // set date fields - $.each(["posting_date", "due_date", "transaction_date"], function(i, fieldname) { - if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) { - me.frm.set_value(fieldname, get_today()); - } - }); - - // set currency fields - $.each(["currency", "price_list_currency"], function(i, fieldname) { - if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) { - me.frm.set_value(fieldname, wn.defaults.get_default("currency")); - } - }); - - // status - if(!this.frm.doc.status) this.frm.set_value("status", "Draft"); - - // TODO set depends_on for customer related fields - } - }, - - // 2 - refresh: function() { - erpnext.hide_naming_series(); - this.show_item_wise_taxes(); - this.set_dynamic_labels(); - }, - - // 3 - onload_post_render: function() { - if(this.frm.doc.__islocal && this.frm.doc.company) { - var me = this; - this.frm.call({ - doc: this.frm.doc, - method: "onload_post_render", - freeze: true, - callback: function(r) { - // remove this call when using client side mapper - me.set_default_values(); - me.frm.refresh(); - } - }); - } + + // TODO set depends_on for customer related fields }, validate: function() { @@ -85,6 +43,32 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ // TODO calc adjustment amount }, + customer: function() { + if(this.frm.doc.customer || this.frm.doc.debit_to) { + if(!this.frm.doc.company) { + this.frm.set_value("customer", null); + msgprint(wn._("Please specify Company")); + } else { + var me = this; + var price_list_name = this.frm.doc.price_list_name; + + this.frm.call({ + doc: this.frm.doc, + method: "set_customer_defaults", + freeze: true, + callback: function(r) { + if(!r.exc) { + me.frm.refresh_fields(); + if(me.frm.doc.price_list_name !== price_list_name) me.price_list_name(); + } + } + }); + } + } + + // TODO hide/unhide related fields + }, + barcode: function(doc, cdt, cdn) { this.item_code(doc, cdt, cdn); }, @@ -93,7 +77,7 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ var me = this; var item = wn.model.get_doc(cdt, cdn); if(item.item_code || item.barcode) { - if(!this.validate_company_and_party()) { + if(!this.validate_company_and_party("customer")) { item.item_code = null; refresh_field("item_code", item.name, item.parentfield); } else { @@ -128,90 +112,8 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ } }, - company: function() { - if(this.frm.doc.company) { - var me = this; - var company_currency = this.get_company_currency(); - $.each(["currency", "price_list_currency"], function(i, fieldname) { - if(!me.doc[fieldname]) { - me.frm.set_value(fieldname, company_currency); - } - }); - this.price_list_currency(); - } - }, - - customer: function() { - if(this.frm.doc.customer || this.frm.doc.debit_to) { - if(!this.frm.doc.company) { - this.frm.set_value("customer", null); - msgprint(wn._("Please specify Company")); - } else { - var me = this; - var price_list_name = this.frm.doc.price_list_name; - - this.frm.call({ - doc: this.frm.doc, - method: "set_customer_defaults", - freeze: true, - callback: function(r) { - if(!r.exc) { - me.frm.refresh_fields(); - if(me.frm.doc.price_list_name !== price_list_name) me.price_list_name(); - } - } - }); - } - } - - // TODO hide/unhide related fields - }, - price_list_name: function() { - var me = this; - if(this.frm.doc.price_list_name) { - this.frm.call({ - method: "setup.utils.get_price_list_currency", - args: {args: { - price_list_name: this.frm.doc.price_list_name, - use_for: "selling" - }}, - callback: function(r) { - if(!r.exc) { - me.price_list_currency(); - } - } - }); - } - }, - - currency: function() { - this.price_list_currency(); - }, - - price_list_currency: function() { - // What TODO? should we make price list system non-mandatory? - // this.frm.toggle_reqd("plc_conversion_rate", - // !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); - - if(this.frm.doc.price_list_currency === this.get_company_currency()) { - this.frm.set_value("plc_conversion_rate", 1.0); - this.calculate_taxes_and_totals(); - } else if(this.frm.doc.price_list_currency === this.frm.doc.currency) { - this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate); - this.calculate_taxes_and_totals(); - } - - this.set_dynamic_labels(); - }, - - conversion_rate: function() { - this.price_list_currency(); - this.calculate_taxes_and_totals(); - }, - - plc_conversion_rate: function() { - this.price_list_currency(); + this._super("selling"); }, ref_rate: function(doc, cdt, cdn) { @@ -224,10 +126,6 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ this.calculate_taxes_and_totals(); }, - qty: function(doc, cdt, cdn) { - this.calculate_taxes_and_totals(); - }, - adj_rate: function(doc, cdt, cdn) { this.ref_rate(doc, cdt, cdn); }, @@ -246,19 +144,6 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ this.calculate_taxes_and_totals(); }, - included_in_print_rate: function(doc, cdt, cdn) { - var tax = wn.model.get_doc(cdt, cdn); - try { - this.validate_on_previous_row(tax); - this.validate_inclusive_tax(tax); - this.calculate_taxes_and_totals(); - } catch(e) { - tax.included_in_print_rate = 0; - refresh_field("included_in_print_rate", tax.name, tax.parentfield); - throw e; - } - }, - commission_rate: function() { this.calculate_commission(); refresh_field("total_commission"); @@ -307,49 +192,10 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ } }, - validate_company_and_party: function() { - var me = this; - var valid = true; - $.each(["company", "customer"], function(i, fieldname) { - if(!me.frm.doc[fieldname]) { - valid = false; - msgprint(wn._("Please specify") + ": " + - wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + - ". " + wn._("It is needed to fetch Item Details.")); - } - }); - return valid; - }, - - set_default_values: function() { - $.each(wn.model.get_doclist(this.frm.doctype, this.frm.docname), function(i, doc) { - var updated = wn.model.set_default_values(doc); - if(doc.parentfield) { - refresh_field(doc.parentfield); - } else { - refresh_field(updated); - } - }); - }, - calculate_taxes_and_totals: function() { - this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); - - // TODO validate conversion rate - - this.frm.item_doclist = this.get_item_doclist(); - this.frm.tax_doclist = this.get_tax_doclist(); - - this.calculate_item_values(); - this.initialize_taxes(); - this.determine_exclusive_rate(); - this.calculate_net_total(); - this.calculate_taxes(); - this.calculate_totals(); + this._super(); this.calculate_commission(); this.calculate_contribution(); - this._cleanup(); - this.frm.doc.in_words = this.frm.doc.in_words_export = ""; // TODO // outstanding amount @@ -357,57 +203,21 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ // check for custom_recalc in custom scripts of server this.frm.refresh_fields(); - this.show_item_wise_taxes(); - - }, - - get_item_doclist: function() { - return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, - {parentfield: this.fname}); - }, - - get_tax_doclist: function() { - return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, - {parentfield: "other_charges"}); }, calculate_item_values: function() { var me = this; - - var _set_base = function(item, print_field, base_field) { - // set values in base currency - item[base_field] = flt(item[print_field] * me.frm.doc.conversion_rate, - precision(base_field, item)); - }; - $.each(this.frm.item_doclist, function(i, item) { wn.model.round_floats_in(item); item.export_amount = flt(item.export_rate * item.qty, precision("export_amount", item)); - _set_base(item, "ref_rate", "base_ref_rate"); - _set_base(item, "export_rate", "basic_rate"); - _set_base(item, "export_amount", "amount"); + me._set_in_company_currency(item, "ref_rate", "base_ref_rate"); + me._set_in_company_currency(item, "export_rate", "basic_rate"); + me._set_in_company_currency(item, "export_amount", "amount"); }); }, - initialize_taxes: function() { - var me = this; - $.each(this.frm.tax_doclist, function(i, tax) { - tax.tax_amount = tax.total = 0.0; - tax.item_wise_tax_detail = {}; - - // temporary fields - tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0; - tax.tax_fraction_for_current_item = tax.grand_total_fraction_for_current_item = 0.0; - - me.validate_on_previous_row(tax); - me.validate_inclusive_tax(tax); - - wn.model.round_floats_in(tax); - }); - }, - determine_exclusive_rate: function() { var me = this; $.each(me.frm.item_doclist, function(n, item) { @@ -482,81 +292,6 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ wn.model.round_floats_in(this.frm.doc, ["net_total", "net_total_export"]); }, - calculate_taxes: function() { - var me = this; - - $.each(this.frm.item_doclist, function(n, item) { - var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); - - $.each(me.frm.tax_doclist, function(i, tax) { - // tax_amount represents the amount of tax for the current step - var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map); - - // case when net total is 0 but there is an actual type charge - // in this case add the actual amount to tax.tax_amount - // and tax.grand_total_for_current_item for the first such iteration - if(tax.charge_type == "Actual" && - !(current_tax_amount || me.frm.doc.net_total || tax.tax_amount)) { - var zero_net_total_adjustment = flt(tax.rate, precision("tax_amount", tax)); - current_tax_amount += zero_net_total_adjustment; - } - - // store tax_amount for current item as it will be used for - // charge type = 'On Previous Row Amount' - tax.tax_amount_for_current_item = current_tax_amount; - - // accumulate tax amount into tax.tax_amount - tax.tax_amount += current_tax_amount; - - // Calculate tax.total viz. grand total till that step - // note: grand_total_for_current_item contains the contribution of - // item's amount, previously applied tax and the current tax on that item - if(i==0) { - tax.grand_total_for_current_item = flt(item.amount + current_tax_amount, - precision("total", tax)); - } else { - tax.grand_total_for_current_item = - flt(me.frm.tax_doclist[i-1].grand_total_for_current_item + current_tax_amount, - precision("total", tax)); - } - - // in tax.total, accumulate grand total for each item - tax.total += tax.grand_total_for_current_item; - - // store tax breakup for each item - tax.item_wise_tax_detail[item.item_code || item.item_name] = current_tax_amount; - - }); - }); - }, - - get_current_tax_amount: function(item, tax, item_tax_map) { - var tax_rate = this._get_tax_rate(tax, item_tax_map); - var current_tax_amount = 0.0; - - if(tax.charge_type == "Actual") { - // distribute the tax amount proportionally to each item row - var actual = flt(tax.rate, precision("tax_amount", tax)); - current_tax_amount = this.frm.doc.net_total ? - ((item.amount / this.frm.doc.net_total) * actual) : - 0.0; - - } else if(tax.charge_type == "On Net Total") { - current_tax_amount = (tax_rate / 100.0) * item.amount; - - } else if(tax.charge_type == "On Previous Row Amount") { - current_tax_amount = (tax_rate / 100.0) * - this.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item; - - } else if(tax.charge_type == "On Previous Row Total") { - current_tax_amount = (tax_rate / 100.0) * - this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; - - } - - return flt(current_tax_amount, precision("tax_amount", tax)); - }, - calculate_totals: function() { var tax_count = this.frm.tax_doclist.length; this.frm.doc.grand_total = flt( @@ -601,91 +336,8 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ }, _cleanup: function() { - $.each(this.frm.tax_doclist, function(i, tax) { - var tax_fields = keys(tax); - $.each(["tax_amount_for_current_item", "grand_total_for_current_item", - "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"], - function(i, fieldname) { delete tax[fieldname];}); - - tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail); - }); - }, - - validate_on_previous_row: function(tax) { - // validate if a valid row id is mentioned in case of - // On Previous Row Amount and On Previous Row Total - if((["On Previous Row Amount", "On Previous Row Total"].indexOf(tax.charge_type) != -1) && - (!tax.row_id || cint(tax.row_id) >= tax.idx)) { - var msg = repl(wn._("Row") + " # %(idx)s [%(doctype)s]: " + - wn._("Please specify a valid") + " %(row_id_label)s", { - idx: tax.idx, - doctype: tax.doctype, - row_id_label: wn.meta.get_label(tax.doctype, "row_id", tax.name) - }); - msgprint(msg); - throw msg; - } - }, - - validate_inclusive_tax: function(tax) { - if(!this.frm.tax_doclist) this.frm.tax_doclist = this.get_tax_doclist(); - - var actual_type_error = function() { - var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " + - "%(charge_type_label)s = \"%(charge_type)s\" " + - wn._("cannot be included in Item's rate"), { - idx: tax.idx, - doctype: tax.doctype, - charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name), - charge_type: tax.charge_type - }); - msgprint(msg); - throw msg; - }; - - var on_previous_row_error = function(row_range) { - var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " + - wn._("to be included in Item's rate, it is required that: ") + - " [" + wn._("Row") + " # %(row_range)s] " + wn._("also be included in Item's rate"), { - idx: tax.idx, - doctype: tax.doctype, - charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name), - charge_type: tax.charge_type, - inclusive_label: wn.meta.get_label(tax.doctype, "included_in_print_rate", tax.name), - row_range: row_range, - }); - - msgprint(msg); - throw msg; - }; - - if(cint(tax.included_in_print_rate)) { - if(tax.charge_type == "Actual") { - // inclusive tax cannot be of type Actual - actual_type_error(); - } else if(tax.charge_type == "On Previous Row Amount" && - !cint(this.frm.tax_doclist[tax.row_id - 1].included_in_print_rate)) { - // referred row should also be an inclusive tax - on_previous_row_error(tax.row_id); - } else if(tax.charge_type == "On Previous Row Total") { - var taxes_not_included = $.map(this.frm.tax_doclist.slice(0, tax.row_id), - function(t) { return cint(t.included_in_print_rate) ? null : t; }); - if(taxes_not_included.length > 0) { - // all rows above this tax should be inclusive - on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id); - } - } - } - }, - - _load_item_tax_rate: function(item_tax_rate) { - return item_tax_rate ? JSON.parse(item_tax_rate) : {}; - }, - - _get_tax_rate: function(tax, item_tax_map) { - return (keys(item_tax_map).indexOf(tax.account_head) != -1) ? - flt(item_tax_map[tax.account_head], precision("rate", tax)) : - tax.rate; + this._super(); + this.frm.doc.in_words = this.frm.doc.in_words_export = ""; }, show_item_wise_taxes: function() { @@ -693,40 +345,6 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ .appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty()); }, - get_item_wise_taxes_html: function() { - var item_tax = {}; - var tax_accounts = []; - var company_currency = this.get_company_currency(); - - $.each(this.get_tax_doclist(), function(i, tax) { - var tax_amount_precision = precision("tax_amount", tax); - $.each(JSON.parse(tax.item_wise_tax_detail || '{}'), - function(item_code, tax_amount) { - if(!item_tax[item_code]) item_tax[item_code] = {}; - item_tax[item_code][tax.account_head] = flt(tax_amount, tax_amount_precision); - }); - tax_accounts.push(tax.account_head); - }); - - var headings = $.map([wn._("Item Name")].concat(tax_accounts), - function(head) { return '' + (head || "") + "" }).join("\n"); - - var rows = $.map(this.get_item_doclist(), function(item) { - var item_tax_record = item_tax[item.item_code || item.item_name]; - return repl("%(item_name)s%(taxes)s", { - item_name: item.item_name, - taxes: $.map(tax_accounts, function(head) { - return "" + format_currency(item_tax_record[head], company_currency) + "" - }).join("\n") - }); - }).join("\n"); - - return '
\ - ' + headings + ' \ - ' + rows + ' \ -
'; - }, - get_charges: function() { var me = this; if(this.frm.doc.charge) { @@ -837,9 +455,6 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ }); }, - get_company_currency: function() { - return erpnext.get_currency(this.frm.doc.company); - } }); // to save previous state of cur_frm.cscript diff --git a/selling/doctype/sales_order/sales_order.js b/selling/doctype/sales_order/sales_order.js index b792754384..79f20bc02b 100644 --- a/selling/doctype/sales_order/sales_order.js +++ b/selling/doctype/sales_order/sales_order.js @@ -28,7 +28,7 @@ wn.require('app/utilities/doctype/sms_control/sms_control.js'); cur_frm.cscript.onload = function(doc, cdt, cdn) { - cur_frm.cscript.manage_rounded_total(); + cur_frm.cscript.toggle_rounded_total(); if(!doc.status) set_multiple(cdt,cdn,{status:'Draft'}); if(!doc.transaction_date) set_multiple(cdt,cdn,{transaction_date:get_today()}); @@ -42,25 +42,10 @@ cur_frm.cscript.onload = function(doc, cdt, cdn) { } } -cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { - var callback = function(doc, cdt, cdn) { - if(doc.__islocal) { - // defined in sales_common.js - cur_frm.cscript.update_item_details(doc, cdt, cdn); - } - } - - cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn, callback); - -} - - cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.clear_custom_buttons(); erpnext.hide_naming_series(); - if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn); - cur_frm.toggle_display("contact_info", doc.customer); if(doc.docstatus==1) { diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index 0a31dfe80e..df5e7614b4 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -43,15 +43,6 @@ cur_frm.cscript.onload = function(doc, dt, dn) { } } -cur_frm.cscript.onload_post_render = function(doc, dt, dn) { - // defined in sales_common.js - var callback = function(doc, dt, dn) { - if(doc.__islocal) cur_frm.cscript.update_item_details(doc, dt, dn); - } - - cur_frm.cscript.hide_price_list_currency(doc, dt, dn, callback); -} - // REFRESH // ================================================================================================ cur_frm.cscript.refresh = function(doc, cdt, cdn) { diff --git a/stock/doctype/material_request/material_request.js b/stock/doctype/material_request/material_request.js index 21bc141659..3d4eed4842 100644 --- a/stock/doctype/material_request/material_request.js +++ b/stock/doctype/material_request/material_request.js @@ -57,14 +57,6 @@ var new_cscript = new erpnext.buying.MaterialRequestController({frm: cur_frm}); $.extend(cur_frm.cscript, new_cscript); -cur_frm.cscript.onload = function(doc, cdt, cdn) { - if (!doc.transaction_date) doc.transaction_date = dateutil.obj_to_str(new Date()); - if (!doc.status) doc.status = 'Draft'; - - // defined in purchase_common.js - //cur_frm.cscript.update_item_details(doc, cdt, cdn); -}; - cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { // second call if(doc.__islocal){ @@ -79,12 +71,6 @@ cur_frm.cscript.get_item_defaults = function(doc) { } }; -cur_frm.cscript.transaction_date = function(doc,cdt,cdn){ - if(doc.__islocal){ - cur_frm.cscript.get_default_schedule_date(doc); - } -}; - cur_frm.cscript.qty = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; if (flt(d.qty) < flt(d.min_order_qty)) diff --git a/stock/doctype/material_request/material_request.py b/stock/doctype/material_request/material_request.py index 8a899b3a40..9f19a5b10b 100644 --- a/stock/doctype/material_request/material_request.py +++ b/stock/doctype/material_request/material_request.py @@ -17,9 +17,6 @@ class DocType(BuyingController): self.tname = 'Material Request Item' self.fname = 'indent_details' - def get_default_schedule_date(self): - get_obj(dt = 'Purchase Common').get_default_schedule_date(self) - # get available qty at warehouse def get_bin_details(self, arg = ''): return get_obj(dt='Purchase Common').get_bin_details(arg) @@ -30,22 +27,12 @@ class DocType(BuyingController): self.check_if_already_pulled() if self.doc.sales_order_no: get_obj('DocType Mapper', 'Sales Order-Material Request', with_children=1).dt_map('Sales Order', 'Material Request', self.doc.sales_order_no, self.doc, self.doclist, "[['Sales Order', 'Material Request'],['Sales Order Item', 'Material Request Item']]") - self.get_item_defaults() else: msgprint("Please select Sales Order whose details need to pull") def check_if_already_pulled(self): pass#if self.[d.sales_order_no for d in getlist(self.doclist, 'indent_details')] - - # Get item's other details - #- ------------------------ - def get_item_defaults(self): - self.get_default_schedule_date() - for d in getlist(self.doclist, 'indent_details'): - det = webnotes.conn.sql("select min_order_qty from tabItem where name = '%s'" % d.item_code) - d.min_order_qty = det and flt(det[0][0]) or 0 - # Validate so items # ---------------------------- def validate_qty_against_so(self): diff --git a/stock/doctype/purchase_receipt/purchase_receipt.js b/stock/doctype/purchase_receipt/purchase_receipt.js index 82e494cfb7..20740e76ab 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/stock/doctype/purchase_receipt/purchase_receipt.js @@ -38,20 +38,6 @@ erpnext.buying.PurchaseReceiptController = erpnext.buying.BuyingController.exten unhide_field(['challan_no', 'challan_date']); } }, - onload_post_render: function(doc, dt, dn) { - var me = this; - var callback = function(doc, dt, dn) { - me.update_item_details(doc, dt, dn, function(r,rt) { }); - } - - // TODO: improve this - if(this.frm.doc.__islocal) { - if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) - this.price_list_name(callback); - else - callback(doc, dt, dn); - } - } }); var new_cscript = new erpnext.buying.PurchaseReceiptController({frm: cur_frm}); From da6e4670a3d380d13716d99d94745000bd1179df Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 27 May 2013 16:34:15 +0530 Subject: [PATCH 13/17] [report][fix] filter fixes in sales/pur register --- .../purchase_register/purchase_register.py | 29 +++++++++------ .../report/sales_register/sales_register.py | 35 +++++++++++-------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index d4f23927d5..7c4b38671d 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -17,6 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import flt +from webnotes import msgprint, _ def execute(filters=None): if not filters: filters = {} @@ -24,6 +25,11 @@ def execute(filters=None): invoice_list = get_invoices(filters) columns, expense_accounts, tax_accounts = get_columns(invoice_list) + + if not invoice_list: + msgprint(_("No record found")) + return columns, invoice_list + invoice_expense_map = get_invoice_expense_map(invoice_list) invoice_tax_map = get_invoice_tax_map(invoice_list) invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) @@ -64,17 +70,18 @@ def get_columns(invoice_list): "Project:Link/Project:80", "Bill No::120", "Bill Date:Date:80", "Remarks::150", "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100" ] - - expense_accounts = webnotes.conn.sql_list("""select distinct expense_head - from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' - and parent in (%s) order by expense_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + expense_accounts = tax_accounts = [] + if invoice_list: + expense_accounts = webnotes.conn.sql_list("""select distinct expense_head + from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' + and parent in (%s) order by expense_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) - tax_accounts = webnotes.conn.sql_list("""select distinct account_head - from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' - and docstatus = 1 and ifnull(account_head, '') != '' and parent in (%s) - order by account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' + and docstatus = 1 and ifnull(account_head, '') != '' and parent in (%s) + order by account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) columns = columns + [(account + ":Currency:120") for account in expense_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ @@ -86,7 +93,7 @@ def get_conditions(filters): conditions = "" if filters.get("company"): conditions += " and company=%(company)s" - if filters.get("account"): conditions += " and account = %(account)s" + if filters.get("account"): conditions += " and credit_to = %(account)s" if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s" if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s" diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 3946f0033d..99057f9a2e 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -17,6 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import flt +from webnotes import msgprint, _ def execute(filters=None): if not filters: filters = {} @@ -24,6 +25,10 @@ def execute(filters=None): invoice_list = get_invoices(filters) columns, income_accounts, tax_accounts = get_columns(invoice_list) + if not invoice_list: + msgprint(_("No record found")) + return columns, invoice_list + invoice_income_map = get_invoice_income_map(invoice_list) invoice_tax_map = get_invoice_tax_map(invoice_list) @@ -69,15 +74,17 @@ def get_columns(invoice_list): "Remarks::150", "Sales Order:Link/Sales Order:100", "Delivery Note:Link/Delivery Note:100" ] - income_accounts = webnotes.conn.sql_list("""select distinct income_account - from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) - order by income_account""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + income_accounts = tax_accounts = [] + if invoice_list: + income_accounts = webnotes.conn.sql_list("""select distinct income_account + from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) + order by income_account""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) - tax_accounts = webnotes.conn.sql_list("""select distinct account_head - from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' - and docstatus = 1 and parent in (%s) order by account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' + and docstatus = 1 and parent in (%s) order by account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) columns = columns + [(account + ":Currency:120") for account in income_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ @@ -89,19 +96,19 @@ def get_conditions(filters): conditions = "" if filters.get("company"): conditions += " and company=%(company)s" - if filters.get("account"): conditions += " and account = %(account)s" + if filters.get("account"): conditions += " and debit_to = %(account)s" - if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s" - if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s" + if filters.get("from_date"): conditions += " and posting_date >= %(from_date)s" + if filters.get("to_date"): conditions += " and posting_date <= %(to_date)s" return conditions def get_invoices(filters): conditions = get_conditions(filters) return webnotes.conn.sql("""select name, posting_date, debit_to, project_name, customer, - remarks, net_total, other_charges_total, grand_total - from `tabSales Invoice` where docstatus = 1 %s - order by posting_date desc, name desc""" % conditions, filters, as_dict=1) + remarks, net_total, other_charges_total, grand_total from `tabSales Invoice` + where docstatus = 1 %s order by posting_date desc, name desc""" % + conditions, filters, as_dict=1) def get_invoice_income_map(invoice_list): income_details = webnotes.conn.sql("""select parent, income_account, sum(amount) as amount From 996191e8b7aee0c4615e3a9e62874f8b26de55c1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 27 May 2013 18:28:36 +0530 Subject: [PATCH 14/17] [purchase invoice] [fix] error message for missing expense account for an item --- accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index c53b6d94fc..95b56dc4ff 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -326,7 +326,7 @@ class DocType(BuyingController): against_accounts.append(stock_not_billed_account) elif not item.expense_head: - msgprint(_("""Expense account is mandatory for item: """) + item.item_code, + msgprint(_("""Expense account is mandatory for item: """) + (item.item_code or item.item_name), raise_exception=1) elif item.expense_head not in against_accounts: From fc77718a3d22c1837fac6635096c33a72ad49d79 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 27 May 2013 19:29:07 +0530 Subject: [PATCH 15/17] [selling/buying] [fixes] updated js files of various doctypes related to selling/g/buying --- .../purchase_invoice/purchase_invoice.js | 82 +-- .../doctype/sales_invoice/sales_invoice.js | 92 +-- .../doctype/sales_invoice/sales_invoice.py | 9 - .../purchase_common/purchase_common.js | 650 ++++-------------- .../purchase_common/purchase_common.py | 26 +- .../doctype/purchase_order/purchase_order.js | 6 +- .../supplier_quotation/supplier_quotation.js | 13 +- buying/utils.py | 24 +- controllers/selling_controller.py | 3 + public/js/transaction.js | 33 +- selling/doctype/quotation/quotation.js | 140 +--- selling/doctype/sales_common/sales_common.js | 32 + selling/doctype/sales_common/sales_common.py | 175 ----- selling/doctype/sales_order/sales_order.js | 129 ++-- selling/doctype/sales_order/sales_order.py | 16 - selling/utils.py | 16 +- stock/doctype/delivery_note/delivery_note.js | 97 +-- stock/doctype/delivery_note/delivery_note.py | 28 +- .../material_request/material_request.js | 15 - .../purchase_receipt/purchase_receipt.js | 140 ++-- utilities/transaction_base.py | 15 +- 21 files changed, 451 insertions(+), 1290 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index 3ddf907fdd..b714a901c5 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -17,10 +17,28 @@ cur_frm.cscript.tname = "Purchase Invoice Item"; cur_frm.cscript.fname = "entries"; cur_frm.cscript.other_fname = "purchase_tax_details"; + wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); wn.require('app/buying/doctype/purchase_common/purchase_common.js'); -erpnext.buying.PurchaseInvoiceController = erpnext.buying.BuyingController.extend({ +wn.provide("erpnext.accounts"); +erpnext.accounts.PurchaseInvoiceController = erpnext.buying.BuyingController.extend({ + setup: function() { + this._super(); + + }, + + onload: function() { + this._super(); + + if(!this.frm.doc.__islocal) { + // show credit_to in print format + if(!this.frm.doc.supplier && this.frm.doc.credit_to) { + this.frm.set_df_property("credit_to", "print_hide", 0); + } + } + }, + refresh: function(doc) { this._super(); @@ -34,6 +52,20 @@ erpnext.buying.PurchaseInvoiceController = erpnext.buying.BuyingController.exten cur_frm.cscript.is_opening(doc); }, + + credit_to: function() { + this.supplier(); + }, + + write_off_amount: function() { + this.calculate_outstanding_amount(); + this.frm.refresh_fields(); + }, + + allocated_amount: function() { + this.calculate_total_advance(); + this.frm.refresh_fields(); + }, }); // for backward compatibility: combine new and previous states @@ -55,23 +87,6 @@ cur_frm.fields_dict.contact_person.on_new = function(dn) { } -cur_frm.cscript.credit_to = function(doc,dt,dn) { - - var callback = function(doc, dt, dn) { - var doc = locals[doc.doctype][doc.name]; - if(doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }), '', doc, dt, dn, 1, function() { - cur_frm.refresh(); - }); - unhide_field(['supplier_address','contact_person']); - } - cur_frm.refresh(); - } - - get_server_fields('get_cust', '', '', doc, dt, dn, 1, callback); -} - cur_frm.fields_dict['entries'].grid.onrowadd = function(doc, cdt, cdn){ cl = getchildren('Purchase Invoice Item', doc.name, cur_frm.cscript.fname, doc.doctype); @@ -95,17 +110,6 @@ cur_frm.cscript.is_opening = function(doc, dt, dn) { if (doc.is_opening == 'Yes') unhide_field('aging_date'); } -cur_frm.cscript.write_off_amount = function(doc) { - doc.total_amount_to_pay = flt(doc.grand_total) - flt(doc.write_off_amount); - doc.outstanding_amount = flt(doc.total_amount_to_pay) - flt(doc.total_advance); - refresh_many(['outstanding_amount', 'total_amount_to_pay']); -} - -cur_frm.cscript.recalculate = function(doc, cdt, cdn) { - cur_frm.cscript.calculate_tax(doc,cdt,cdn); - calc_total_advance(doc,cdt,cdn); -} - cur_frm.cscript.get_items = function(doc, dt, dn) { var callback = function(r,rt) { unhide_field(['supplier_address', 'contact_person']); @@ -114,11 +118,6 @@ cur_frm.cscript.get_items = function(doc, dt, dn) { $c_obj(make_doclist(dt,dn),'pull_details','',callback); } -cur_frm.cscript.allocated_amount = function(doc,cdt,cdn) { - calc_total_advance(doc, cdt, cdn); -} - - cur_frm.cscript.make_bank_voucher = function() { wn.call({ method: "accounts.doctype.journal_voucher.journal_voucher.get_default_bank_cash_account", @@ -204,21 +203,6 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn){ refresh_field('entries'); } -calc_total_advance = function(doc,cdt,cdn) { - var doc = locals[doc.doctype][doc.name]; - var el = getchildren('Purchase Invoice Advance',doc.name,'advance_allocation_details') - var total_advance = 0; - for(var i in el) { - if (! el[i].allocated_amount == 0) { - total_advance += flt(el[i].allocated_amount); - } - } - doc.total_amount_to_pay = flt(doc.grand_total) - flt(doc.write_off_amount); - doc.total_advance = flt(total_advance); - doc.outstanding_amount = flt(doc.total_amount_to_pay) - flt(total_advance); - refresh_many(['total_advance','outstanding_amount', 'total_amount_to_pay']); -} - cur_frm.cscript.make_jv = function(doc, dt, dn, bank_account) { var jv = wn.model.make_new_doc_and_get_name('Journal Voucher'); jv = locals['Journal Voucher'][jv]; diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index 800cd2bba5..10853ea1f6 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -22,17 +22,20 @@ cur_frm.cscript.sales_team_fname = "sales_team"; // print heading cur_frm.pformat.print_heading = 'Invoice'; -wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/selling/doctype/sales_common/sales_common.js'); -erpnext.selling.SalesInvoiceController = erpnext.selling.SellingController.extend({ +wn.provide("erpnext.accounts"); +erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({ onload: function() { this._super(); - // show debit_to in print format - if(!this.frm.doc.customer && this.frm.doc.debit_to) { - this.frm.set_df_property("debit_to", "print_hide", 0); + if(!this.frm.doc.__islocal) { + // show debit_to in print format + if(!this.frm.doc.customer && this.frm.doc.debit_to) { + this.frm.set_df_property("debit_to", "print_hide", 0); + } } }, @@ -41,10 +44,6 @@ erpnext.selling.SalesInvoiceController = erpnext.selling.SellingController.exten cur_frm.cscript.is_opening(doc, dt, dn); - // Show / Hide button - cur_frm.clear_custom_buttons(); - // if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, dt, dn); - if(doc.docstatus==1) { cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry); cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); @@ -78,10 +77,39 @@ erpnext.selling.SalesInvoiceController = erpnext.selling.SellingController.exten debit_to: function() { this.customer(); }, + + allocated_amount: function() { + this.calculate_total_advance(); + this.frm.refresh_fields(); + }, + + write_off_outstanding_amount_automatically: function() { + if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { + wn.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); + this.frm.set_value("write_off_amount", + flt(this.frm.doc.grand_total - this.frm.doc.paid_amount), precision("write_off_amount")); + } + + this.frm.runclientscript("write_off_amount"); + + // TODO doubt? + // if write off amount = grand total - paid amount + // then why is outstanding amount = grand total - write off amount - paid amount - advance + // when write off amount already is grand total - paid amount! + }, + + write_off_amount: function() { + this.calculate_outstanding_amount(); + this.frm.refresh_fields(); + }, + + paid_amount: function() { + this.write_off_outstanding_amount_automatically(); + }, }); // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new erpnext.selling.SalesInvoiceController({frm: cur_frm})); +$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm})); // Hide Fields // ------------ @@ -129,35 +157,10 @@ cur_frm.cscript.update_stock = function(doc, dt, dn) { cur_frm.cscript.hide_fields(doc, dt, dn); } -cur_frm.cscript.warehouse = function(doc, cdt , cdn) { - var d = locals[cdt][cdn]; - if (!d.item_code) { msgprint("please enter item code first"); return }; - if (d.warehouse) { - arg = "{'item_code':'" + d.item_code + "','warehouse':'" + d.warehouse +"'}"; - get_server_fields('get_actual_qty',arg,'entries',doc,cdt,cdn,1); - } -} - cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); } -cur_frm.cscript.write_off_outstanding_amount_automatically = function(doc) { - if (doc.write_off_outstanding_amount_automatically == 1) - doc.write_off_amount = flt(doc.grand_total) - flt(doc.paid_amount); - - doc.outstanding_amount = flt(doc.grand_total) - flt(doc.paid_amount) - flt(doc.write_off_amount); - refresh_field(['write_off_amount', 'outstanding_amount']); -} - -cur_frm.cscript.paid_amount = function(doc) { - cur_frm.cscript.write_off_outstanding_amount_automatically(doc); -} - -cur_frm.cscript.write_off_amount = function(doc) { - cur_frm.cscript.write_off_outstanding_amount_automatically(doc); -} - cur_frm.fields_dict['entries'].grid.onrowadd = function(doc, cdt, cdn){ cl = getchildren('Sales Invoice Item', doc.name, cur_frm.cscript.fname, doc.doctype); @@ -193,12 +196,6 @@ cur_frm.cscript.get_items = function(doc, dt, dn) { -// Allocated Amount in advances table -// ----------------------------------- -cur_frm.cscript.allocated_amount = function(doc,cdt,cdn){ - cur_frm.cscript.calc_adjustment_amount(doc,cdt,cdn); -} - //Make Delivery Note Button //----------------------------- @@ -363,19 +360,6 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn){ refresh_field(cur_frm.cscript.fname); } -cur_frm.cscript.calc_adjustment_amount = function(doc,cdt,cdn) { - var doc = locals[doc.doctype][doc.name]; - var el = getchildren('Sales Invoice Advance',doc.name,'advance_adjustment_details'); - var total_adjustment_amt = 0 - for(var i in el) { - total_adjustment_amt += flt(el[i].allocated_amount) - } - doc.total_advance = flt(total_adjustment_amt); - doc.outstanding_amount = flt(doc.grand_total) - flt(total_adjustment_amt) - flt(doc.paid_amount) - flt(doc.write_off_amount); - refresh_many(['total_advance','outstanding_amount']); -} - - // Make Journal Voucher // -------------------- cur_frm.cscript.make_jv = function(doc, dt, dn, bank_account) { diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index d313a3259e..55b233621b 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -599,15 +599,6 @@ class DocType(SellingController): get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values) - def get_actual_qty(self,args): - args = eval(args) - actual_qty = webnotes.conn.sql("select actual_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (args['item_code'], args['warehouse']), as_dict=1) - ret = { - 'actual_qty' : actual_qty and flt(actual_qty[0]['actual_qty']) or 0 - } - return ret - - def make_gl_entries(self): from accounts.general_ledger import make_gl_entries, merge_similar_entries diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index 320212254f..65b44c3d8c 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -27,24 +27,34 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ var me = this; if(this.frm.fields_dict.price_list_name) { - this.frm.fields_dict.price_list_name.get_query = function() { + this.frm.set_query("price_list_name", function() { return repl("select distinct price_list_name from `tabItem Price` \ where buying = 1 and price_list_name like \"%s%%\""); - }; - } - - if(this.frm.fields_dict.price_list_currency) { - this.frm.fields_dict.price_list_currency.get_query = function() { + }); + + this.frm.set_query("price_list_currency", function() { return repl("select distinct ref_currency from `tabItem Price` \ where price_list_name=\"%(price_list_name)s\" and buying = 1 \ and ref_currency like \"%s%%\"", {price_list_name: me.frm.doc.price_list_name}); - }; + }); } + + if(this.frm.fields_dict.supplier) { + this.frm.set_query("supplier", erpnext.utils.supplier_query); + } + + this.frm.set_query("item_code", this.frm.cscript.fname, function() { + if(me.frm.doc.is_subcontracted == "Yes") { + return erpnext.queries.item({'ifnull(tabItem.is_sub_contracted_item, "No")': "Yes"}); + } else { + return erpnext.queries.item({'ifnull(tabItem.is_purchase_item, "No")': "Yes"}); + } + }); }, validate: function() { - + this.calculate_taxes_and_totals(); }, supplier: function() { @@ -100,7 +110,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ }, callback: function(r) { if(!r.exc) { - me.ref_rate(me.frm.doc, cdt, cdn); + me.import_ref_rate(me.frm.doc, cdt, cdn); } } }); @@ -108,13 +118,102 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } }, - price_list_name: function(callback_fn) { + price_list_name: function() { this._super("buying"); }, + import_ref_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["import_ref_rate", "discount_rate"]); + + item.import_rate = flt(item.import_ref_rate * (1 - item.discount_rate / 100.0), + precision("import_rate", item)); + + this.calculate_taxes_and_totals(); + }, + + discount_rate: function(doc, cdt, cdn) { + this.import_rate(doc, cdt, cdn); + }, + + import_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["import_rate", "discount_rate"]); + + if(item.import_ref_rate) { + item.discount_rate = flt((1 - item.import_rate / item.import_ref_rate) * 100.0, + precision("discount_rate", item)); + } else { + item.discount_rate = 0.0; + } + + this.calculate_taxes_and_totals(); + }, + + uom: function(doc, cdt, cdn) { + var me = this; + var item = wn.model.get_doc(cdt, cdn); + if(item.item_code && item.uom) { + this.frm.call({ + method: "buying.utils.get_conversion_factor", + child: item, + args: { + item_code: item.item_code, + uom: item.uom, + }, + callback: function(r) { + if(!r.exc) { + me.conversion_factor(me.frm.doc, cdt, cdn); + } + } + }); + } + }, + + qty: function(doc, cdt, cdn) { + this._super(doc, cdt, cdn); + this.conversion_factor(doc, cdt, cdn); + }, + + conversion_factor: function(doc, cdt, cdn) { + if(wn.meta.get_docfield(cdt, "stock_qty", cdn)) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["qty", "conversion_factor"]) + item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); + refresh_field("stock_qty", item.name, item.parentfield); + } + }, + + warehouse: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + if(item.item_code && item.warehouse) { + this.frm.call({ + method: "buying.utils.get_conversion_factor", + child: item, + args: { + item_code: item.item_code, + warehouse: item.warehouse, + } + }); + } + }, + + project_name: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + if(item.project_name) { + $.each(wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, {parentfield: this.fname}), + function(i, other_item) { + if(!other_item.project_name) { + other_item.project_name = item.project_name; + refresh_field("project_name", other_item.name, other_item.parentfield); + } + }); + } + }, + calculate_taxes_and_totals: function() { this._super(); - this.calculate_outstanding_amount(); + this.calculate_total_advance(); this.frm.refresh_fields(); }, @@ -164,6 +263,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ this.frm.doc.total_tax = flt(this.frm.doc.grand_total - this.frm.doc.net_total, precision("total_tax")); + // rounded totals if(wn.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) { this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total); } @@ -171,6 +271,22 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ if(wn.meta.get_docfield(this.frm.doc.doctype, "rounded_total_import", this.frm.doc.name)) { this.frm.doc.rounded_total_import = Math.round(this.frm.doc.grand_total_import); } + + // other charges added/deducted + if(tax_count) { + this.frm.doc.other_charges_added = wn.utils.sum($.map(this.frm.tax_doclist, + function(tax) { return tax.add_deduct_tax == "Add" ? tax.tax_amount : 0.0; })); + + this.frm.doc.other_charges_deducted = wn.utils.sum($.map(this.frm.tax_doclist, + function(tax) { return tax.add_deduct_tax == "Deduct" ? tax.tax_amount : 0.0; })); + + wn.model.round_floats_in(this.frm.doc, ["other_charges_added", "other_charges_deducted"]); + + this.frm.doc.other_charges_added_import = flt(this.frm.doc.other_charges_added / this.frm.doc.conversion_rate, + precision("other_charges_added_import")); + this.frm.doc.other_charges_deducted_import = flt(this.frm.doc.other_charges_deducted / this.frm.doc.conversion_rate, + precision("other_charges_deducted_import")); + } }, _cleanup: function() { @@ -189,9 +305,13 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } }, + calculate_total_advance: function() { + this._super("Purchase Invoice", "advance_allocation_details"); + }, + calculate_outstanding_amount: function() { if(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.docstatus < 2) { - wn.model.round_floats_in(this.frm.doc, ["total_advance", "write_off_amount"]); + wn.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]); this.frm.doc.total_amount_to_pay = flt(this.frm.doc.grand_total - this.frm.doc.write_off_amount, precision("total_amount_to_pay")); this.frm.doc.outstanding_amount = flt(this.frm.doc.total_amount_to_pay - this.frm.doc.total_advance, @@ -210,6 +330,15 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } }, + show_item_wise_taxes: function() { + $(this.get_item_wise_taxes_html()) + .appendTo($(this.frm.fields_dict.tax_calculation.wrapper).empty()); + }, + + recalculate: function() { + this.calculate_taxes_and_totals(); + }, + set_dynamic_labels: function(doc, dt, dn) { var company_currency = this.get_company_currency(); @@ -305,6 +434,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ $wrapper.find('[data-grid-fieldname="'+fname+'"]').text(label); }); }, + + }); // to save previous state of cur_frm.cscript @@ -319,498 +450,3 @@ $.extend(cur_frm.cscript, prev_cscript); var tname = cur_frm.cscript.tname; var fname = cur_frm.cscript.fname; - -//==================== Item Code Get Query ======================================================= -// Only Is Purchase Item = 'Yes' and Items not moved to trash are allowed. -cur_frm.fields_dict[fname].grid.get_field("item_code").get_query = function(doc, cdt, cdn) { - if (doc.is_subcontracted =="Yes") { - return erpnext.queries.item({ - 'ifnull(tabItem.is_sub_contracted_item, "No")': 'Yes' - }) - } else { - return erpnext.queries.item({ - 'ifnull(tabItem.is_purchase_item, "No")': 'Yes' - }) - } -} - -//==================== Update Stock Qty ========================================================== -cur_frm.cscript.update_stock_qty = function(doc,cdt,cdn){ - d = locals[cdt][cdn] - // Step 1:=> Check if qty , uom, conversion_factor - if (d.qty && d.uom && d.conversion_factor){ - // Step 2:=> Set stock_qty = qty * conversion_factor - d.stock_qty = flt(flt(d.qty) * flt(d.conversion_factor)); - // Step 3:=> Refer stock_qty field a that particular row. - refresh_field('stock_qty' , d.name,fname); - } -} - -//==================== UOM ====================================================================== -cur_frm.cscript.uom = function(doc, cdt, cdn, args) { - if(!args) args = {}; - - // args passed can contain conversion_factor - var d = locals[cdt][cdn]; - $.extend(args, { - item_code: d.item_code, - uom: d.uom, - stock_qty: flt(d.stock_qty), - }); - - if(d.item_code && d.uom) { - cur_frm.call({ - method: "buying.doctype.purchase_common.purchase_common.get_uom_details", - args: { args: args }, - child: d, - callback: function(r) { - cur_frm.cscript.calc_amount(doc, 2); - } - }); - } -} - - -//==================== Conversion factor ========================================================= -cur_frm.cscript.conversion_factor = function(doc, cdt, cdn) { - var item = locals[cdt][cdn]; - - cur_frm.cscript.uom(doc, cdt, cdn, { conversion_factor: item.conversion_factor }); -} - -//==================== stock qty ====================================================================== -cur_frm.cscript.stock_qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - if(d.uom && d.qty){ - d.conversion_factor = flt(d.stock_qty)/flt(d.qty); - refresh_field('conversion_factor', d.name, fname); - } -} - -//==================== Warehouse ================================================================ -cur_frm.cscript.warehouse = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - if (d.item_code && d.warehouse) { - str_arg = "{'item_code':'" + (d.item_code?d.item_code:'') + "', 'warehouse':'" + (d.warehouse?d.warehouse:'') + "'}" - get_server_fields('get_bin_details', str_arg, fname, doc, cdt, cdn, 1); - } -} - -//=================== Quantity =================================================================== -cur_frm.cscript.qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - // Step 1: => Update Stock Qty - cur_frm.cscript.update_stock_qty(doc,cdt,cdn); - // Step 2: => Calculate Amount - cur_frm.cscript.calc_amount(doc, 2); -} - - -//=================== Purchase Rate ============================================================== -cur_frm.cscript.purchase_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 2); -} - -//==================== Import Rate ================================================================ -cur_frm.cscript.import_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 1); -} - -//==================== Discount Rate ================================================================ -cur_frm.cscript.discount_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 4); -} -//==================== Purchase Ref Rate ================================================================ -cur_frm.cscript.purchase_ref_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 4); -} -//==================== Import Ref Rate ================================================================ -cur_frm.cscript.import_ref_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 5); -} - -//==================== Validate ==================================================================== -cur_frm.cscript.validate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 1); - - // calculate advances if pv - if(doc.docstatus == 0 && doc.doctype == 'Purchase Invoice') calc_total_advance(doc, cdt, cdn); -} - -// **************** RE-CALCULATE VALUES *************************** - -cur_frm.cscript.recalculate_values = function(doc, cdt, cdn) { - cur_frm.cscript.calculate_tax(doc,cdt,cdn); -} - -cur_frm.cscript.calculate_tax = function(doc, cdt, cdn) { - var other_fname = cur_frm.cscript.other_fname; - - var cl = getchildren('Purchase Taxes and Charges', doc.name, other_fname, doc.doctype); - for(var i = 0; i 0) { - var cl = getchildren('Purchase Taxes and Charges', doc.name, other_fname,doc.doctype); - for(var i = 0; i2) alert("You cannot enter more than 2 nos. for division"); - var id1 = cint(row[0].replace(/^\s+|\s+$/g,"")); - var id2 = cint(row[1].replace(/^\s+|\s+$/g,"")); - tax_amount = flt(tax[id1-1].total_amount) / flt(tax[id2-1].total_amount); - } - return tax_amount - } - else if(tax[t].charge_type == 'On Previous Row Total') { - var row = cint(tax[t].row_id); - if(tax[row-1].add_deduct_tax == 'Add'){ - return tax_amount = flt(rate) * (flt(tax[row-1].total_tax_amount)+flt(tax[row-1].total_amount)) / 100; - } - else if(tax[row-1].add_deduct_tax == 'Deduct'){ - return tax_amount = flt(rate) * (flt(tax[row-1].total_tax_amount)-flt(tax[row-1].total_amount)) / 100; - } - } -} - -// ******* Calculation of total amounts of document (item amount + other charges)**************** -cur_frm.cscript.calc_doc_values = function(doc, tname, fname, other_fname) { - doc = locals[doc.doctype][doc.name]; - var net_total = 0; var total_tax = 0; var other_charges_added = 0; - var other_charges_deducted = 0; - var cl = getchildren(tname, doc.name, fname); - for(var i = 0; i 100) { var msg = wn._(wn.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) + diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py index 0ac8031049..5b391bbd3e 100644 --- a/selling/doctype/sales_common/sales_common.py +++ b/selling/doctype/sales_common/sales_common.py @@ -48,26 +48,6 @@ class DocType(TransactionBase): self.msg = [] - - # Get Sales Person Details - # ========================== - - # TODO: To be deprecated if not in use - def get_sales_person_details(self, obj): - if obj.doc.doctype != 'Quotation': - obj.doclist = obj.doc.clear_table(obj.doclist,'sales_team') - idx = 0 - for d in webnotes.conn.sql("select sales_person, allocated_percentage, allocated_amount, incentives from `tabSales Team` where parent = '%s'" % obj.doc.customer): - ch = addchild(obj.doc, 'sales_team', 'Sales Team', obj.doclist) - ch.sales_person = d and cstr(d[0]) or '' - ch.allocated_percentage = d and flt(d[1]) or 0 - ch.allocated_amount = d and flt(d[2]) or 0 - ch.incentives = d and flt(d[3]) or 0 - ch.idx = idx - idx += 1 - return obj.doclist - - # Get customer's contact person details # ============================================================== def get_contact_details(self, obj = '', primary = 0): @@ -100,161 +80,6 @@ class DocType(TransactionBase): if obj.doc.company: acc_head = webnotes.conn.sql("select name from `tabAccount` where name = '%s' and docstatus != 2" % (cstr(obj.doc.customer) + " - " + webnotes.conn.get_value('Company', obj.doc.company, 'abbr'))) obj.doc.debit_to = acc_head and acc_head[0][0] or '' - - - - # Get Item Details - # =============================================================== - # def get_item_details(self, args, obj): - # import json - # if not obj.doc.price_list_name: - # msgprint("Please Select Price List before selecting Items", raise_exception=True) - # item = webnotes.conn.sql("""select description, item_name, brand, item_group, stock_uom, - # default_warehouse, default_income_account, default_sales_cost_center, - # purchase_account, description_html, barcode from `tabItem` - # where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now() - # or end_of_life = '0000-00-00') and (is_sales_item = 'Yes' - # or is_service_item = 'Yes')""", args['item_code'], as_dict=1) - # tax = webnotes.conn.sql("""select tax_type, tax_rate from `tabItem Tax` - # where parent = %s""", args['item_code']) - # t = {} - # for x in tax: t[x[0]] = flt(x[1]) - # ret = { - # 'description': item and item[0]['description_html'] or \ - # item[0]['description'], - # 'barcode': item and item[0]['barcode'] or '', - # 'item_group': item and item[0]['item_group'] or '', - # 'item_name': item and item[0]['item_name'] or '', - # 'brand': item and item[0]['brand'] or '', - # 'stock_uom': item and item[0]['stock_uom'] or '', - # 'reserved_warehouse': item and item[0]['default_warehouse'] or '', - # 'warehouse': item and item[0]['default_warehouse'] or \ - # args.get('warehouse'), - # 'income_account': item and item[0]['default_income_account'] or \ - # args.get('income_account'), - # 'expense_account': item and item[0]['purchase_account'] or \ - # args.get('expense_account'), - # 'cost_center': item and item[0]['default_sales_cost_center'] or \ - # args.get('cost_center'), - # # this is done coz if item once fetched is fetched again than its qty shld be reset to 1 - # 'qty': 1.00, - # 'adj_rate': 0, - # 'amount': 0, - # 'export_amount': 0, - # 'item_tax_rate': json.dumps(t), - # 'batch_no': '' - # } - # if(obj.doc.price_list_name and item): #this is done to fetch the changed BASIC RATE and REF RATE based on PRICE LIST - # base_ref_rate = self.get_ref_rate(args['item_code'], obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate) - # ret['ref_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - # ret['export_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - # ret['base_ref_rate'] = flt(base_ref_rate) - # ret['basic_rate'] = flt(base_ref_rate) - # - # if ret['warehouse'] or ret['reserved_warehouse']: - # av_qty = self.get_available_qty({'item_code': args['item_code'], 'warehouse': ret['warehouse'] or ret['reserved_warehouse']}) - # ret.update(av_qty) - # - # # get customer code for given item from Item Customer Detail - # customer_item_code_row = webnotes.conn.sql("""\ - # select ref_code from `tabItem Customer Detail` - # where parent = %s and customer_name = %s""", - # (args['item_code'], obj.doc.customer)) - # if customer_item_code_row and customer_item_code_row[0][0]: - # ret['customer_item_code'] = customer_item_code_row[0][0] - # - # return ret - - # TODO: deprecate it - def get_item_defaults(self, args): - item = webnotes.conn.sql("""select default_warehouse, default_income_account, - default_sales_cost_center, purchase_account from `tabItem` where name = %s - and (ifnull(end_of_life,'') = '' or end_of_life > now() or end_of_life = '0000-00-00') - and (is_sales_item = 'Yes' or is_service_item = 'Yes') """, - (args['item_code']), as_dict=1) - ret = { - 'reserved_warehouse': item and item[0]['default_warehouse'] or '', - 'warehouse': item and item[0]['default_warehouse'] or args.get('warehouse'), - 'income_account': item and item[0]['default_income_account'] or \ - args.get('income_account'), - 'expense_account': item and item[0]['purchase_account'] or args.get('expense_account'), - 'cost_center': item and item[0]['default_sales_cost_center'] or args.get('cost_center'), - } - - return ret - - # def get_available_qty(self,args): -# tot_avail_qty = webnotes.conn.sql("select projected_qty, actual_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (args['item_code'], args['warehouse']), as_dict=1) -# ret = { -# 'projected_qty' : tot_avail_qty and flt(tot_avail_qty[0]['projected_qty']) or 0, -# 'actual_qty' : tot_avail_qty and flt(tot_avail_qty[0]['actual_qty']) or 0 -# } -# return ret - - - # ***************** Get Ref rate as entered in Item Master ******************** - # def get_ref_rate(self, item_code, price_list_name, price_list_currency, plc_conv_rate): - # ref_rate = webnotes.conn.sql("select ref_rate from `tabItem Price` where parent = %s and price_list_name = %s and ref_currency = %s and selling=1", - # (item_code, price_list_name, price_list_currency)) - # base_ref_rate = ref_rate and flt(ref_rate[0][0]) * flt(plc_conv_rate) or 0 - # return base_ref_rate - - # ****** Re-cancellculates Basic Rate & amount based on Price List Selected ****** - # def get_adj_percent(self, obj): - # for d in getlist(obj.doclist, obj.fname): - # base_ref_rate = self.get_ref_rate(d.item_code, obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate) - # d.adj_rate = 0 - # d.ref_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - # d.basic_rate = flt(base_ref_rate) - # d.base_ref_rate = flt(base_ref_rate) - # d.export_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - # d.amount = flt(d.qty)*flt(base_ref_rate) - # d.export_amount = flt(d.qty)*flt(base_ref_rate)/flt(obj.doc.conversion_rate) - - - # # Load Default Taxes - # # ==================== - # def load_default_taxes(self, obj): - # if cstr(obj.doc.charge): - # return self.get_other_charges(obj) - # else: - # return self.get_other_charges(obj, 1) - # - # - # # Get other charges from Master - # # ================================================================================= - # def get_other_charges(self,obj, default=0): - # obj.doclist = obj.doc.clear_table(obj.doclist, 'other_charges') - # if not getlist(obj.doclist, 'other_charges'): - # if default: add_cond = 'ifnull(t2.is_default,0) = 1' - # else: add_cond = 't1.parent = "'+cstr(obj.doc.charge)+'"' - # idx = 0 - # other_charge = webnotes.conn.sql("""\ - # select t1.* - # from - # `tabSales Taxes and Charges` t1, - # `tabSales Taxes and Charges Master` t2 - # where - # t1.parent = t2.name and - # t2.company = '%s' and - # %s - # order by t1.idx""" % (obj.doc.company, add_cond), as_dict=1) - # from webnotes.model import default_fields - # for other in other_charge: - # # remove default fields like parent, parenttype etc. - # # from query results - # for field in default_fields: - # if field in other: del other[field] - # - # d = addchild(obj.doc, 'other_charges', 'Sales Taxes and Charges', - # obj.doclist) - # d.fields.update(other) - # d.rate = flt(d.rate) - # d.tax_amount = flt(d.tax_rate) - # d.included_in_print_rate = cint(d.included_in_print_rate) - # d.idx = idx - # idx += 1 - # return obj.doclist # Get TERMS AND CONDITIONS # ======================================================================================= diff --git a/selling/doctype/sales_order/sales_order.js b/selling/doctype/sales_order/sales_order.js index 79f20bc02b..ded9c447e8 100644 --- a/selling/doctype/sales_order/sales_order.js +++ b/selling/doctype/sales_order/sales_order.js @@ -26,90 +26,54 @@ wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); - -cur_frm.cscript.onload = function(doc, cdt, cdn) { - cur_frm.cscript.toggle_rounded_total(); - - if(!doc.status) set_multiple(cdt,cdn,{status:'Draft'}); - if(!doc.transaction_date) set_multiple(cdt,cdn,{transaction_date:get_today()}); - if(!doc.price_list_currency) set_multiple(cdt, cdn, {price_list_currency: doc.currency, plc_conversion_rate: 1}); - // load default charges - - if(doc.__islocal && !doc.customer){ - hide_field(['customer_address','contact_person', 'customer_name', - 'address_display', 'contact_display', 'contact_mobile', - 'contact_email', 'territory', 'customer_group']); - } -} - -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - cur_frm.clear_custom_buttons(); - erpnext.hide_naming_series(); - - cur_frm.toggle_display("contact_info", doc.customer); - - if(doc.docstatus==1) { - if(doc.status != 'Stopped') { - cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); - // delivery note - if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales') - cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']); +erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({ + refresh: function(doc, dt, dn) { + this._super(); + + if(doc.docstatus==1) { + if(doc.status != 'Stopped') { + cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + // delivery note + if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales') + cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']); - // maintenance - if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) { - cur_frm.add_custom_button('Make Maint. Visit', cur_frm.cscript.make_maintenance_visit); - cur_frm.add_custom_button('Make Maint. Schedule', cur_frm.cscript['Make Maintenance Schedule']); + // maintenance + if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) { + cur_frm.add_custom_button('Make Maint. Visit', cur_frm.cscript.make_maintenance_visit); + cur_frm.add_custom_button('Make Maint. Schedule', cur_frm.cscript['Make Maintenance Schedule']); + } + + // indent + if(!doc.order_type || (doc.order_type == 'Sales')) + cur_frm.add_custom_button('Make ' + wn._('Material Request'), cur_frm.cscript['Make Material Request']); + + // sales invoice + if(flt(doc.per_billed, 2) < 100) + cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']); + + // stop + if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100) + cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']); + } else { + // un-stop + cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']); } - - // indent - if(!doc.order_type || (doc.order_type == 'Sales')) - cur_frm.add_custom_button('Make ' + wn._('Material Request'), cur_frm.cscript['Make Material Request']); - - // sales invoice - if(flt(doc.per_billed, 2) < 100) - cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']); - - // stop - if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100) - cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']); - } else { - // un-stop - cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']); } - } - cur_frm.cscript.order_type(doc); -} - -cur_frm.cscript.order_type = function(doc) { - if(doc.order_type == "Sales") { - cur_frm.toggle_reqd("delivery_date", 1); - } else { - cur_frm.toggle_reqd("delivery_date", 0); - } -} - -//customer -cur_frm.cscript.customer = function(doc,dt,dn) { - cur_frm.toggle_display("contact_info", doc.customer); + this.order_type(doc); + }, - var pl = doc.price_list_name; - var callback = function(r,rt) { - var callback2 = function(r, rt) { - if(doc.customer) - unhide_field(['customer_address', 'contact_person', 'territory','customer_group']); - cur_frm.refresh(); - - if(!onload && (pl != doc.price_list_name)) cur_frm.cscript.price_list_name(doc, dt, dn); + order_type: function() { + this.frm.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales"); + }, + + reserved_warehouse: function(doc, cdt, cdn) { + this.warehouse(doc, cdt, cdn); + }, +}); - } - var doc = locals[cur_frm.doctype][cur_frm.docname]; - get_server_fields('get_shipping_address',doc.customer,'',doc, dt, dn, 0, callback2); - - } - if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), - 'get_default_customer_address', '', callback); -} +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm})); cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); @@ -165,15 +129,6 @@ cur_frm.fields_dict['quotation_no'].get_query = function(doc) { ORDER BY `tabQuotation`.`name` DESC LIMIT 50', {cond:cond}); } - -cur_frm.cscript.reserved_warehouse = function(doc, cdt , cdn) { - var d = locals[cdt][cdn]; - if (d.reserved_warehouse) { - arg = "{'item_code':'" + d.item_code + "','warehouse':'" + d.reserved_warehouse +"'}"; - get_server_fields('get_available_qty',arg,'sales_order_details',doc,cdt,cdn,1); - } -} - //----------- make maintenance schedule---------- cur_frm.cscript['Make Maintenance Schedule'] = function() { var doc = cur_frm.doc; diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py index 4b5408d3d1..79b37be0e5 100644 --- a/selling/doctype/sales_order/sales_order.py +++ b/selling/doctype/sales_order/sales_order.py @@ -58,22 +58,6 @@ class DocType(SellingController): def get_comm_rate(self, sales_partner): return get_obj('Sales Common').get_comm_rate(sales_partner, self) - def get_item_details(self, args=None): - import json - args = args and json.loads(args) or {} - if args.get('item_code'): - return get_obj('Sales Common').get_item_details(args, self) - else: - obj = get_obj('Sales Common') - for doc in self.doclist: - if doc.fields.get('item_code'): - arg = {'item_code':doc.fields.get('item_code'), 'income_account':doc.fields.get('income_account'), - 'cost_center': doc.fields.get('cost_center'), 'warehouse': doc.fields.get('warehouse')}; - ret = obj.get_item_defaults(arg) - for r in ret: - if not doc.fields.get(r): - doc.fields[r] = ret[r] - def get_adj_percent(self, arg=''): get_obj('Sales Common').get_adj_percent(self) diff --git a/selling/utils.py b/selling/utils.py index 5e986eeda7..6901028710 100644 --- a/selling/utils.py +++ b/selling/utils.py @@ -68,7 +68,7 @@ def get_item_details(args): out.update(_get_price_list_rate(args, item_bean, meta)) if out.warehouse or out.reserved_warehouse: - out.update(_get_available_qty(args, out.warehouse or out.reserved_warehouse)) + out.update(get_available_qty(args.item_code, out.warehouse or out.reserved_warehouse)) out.customer_item_code = _get_customer_item_code(args, item_bean) @@ -109,6 +109,7 @@ def _validate_item_details(args, item): def _get_basic_details(args, item_bean): item = item_bean.doc out = webnotes._dict({ + "item_code": item.name, "description": item.description_html or item.description, "reserved_warehouse": item.default_warehouse, "warehouse": item.default_warehouse or args.warehouse, @@ -129,7 +130,7 @@ def _get_basic_details(args, item_bean): return out -def _get_price_list_rate(args, item_bean, meta=None): +def _get_price_list_rate(args, item_bean, meta): base_ref_rate = item_bean.doclist.get({ "parentfield": "ref_rate_details", "price_list_name": args.price_list_name, @@ -143,10 +144,11 @@ def _get_price_list_rate(args, item_bean, meta=None): from utilities.transaction_base import validate_currency validate_currency(args, item_bean.doc, meta) - return {"base_ref_rate": flt(base_ref_rate[0].ref_rate / args.plc_conversion_rate)} - -def _get_available_qty(args, warehouse): - return webnotes.conn.get_value("Bin", {"item_code": args.item_code, "warehouse": warehouse}, + return {"ref_rate": flt(base_ref_rate[0].ref_rate * args.plc_conversion_rate / args.conversion_rate)} + +@webnotes.whitelist() +def get_available_qty(item_code, warehouse): + return webnotes.conn.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, ["projected_qty", "actual_qty"], as_dict=True) or {} def _get_customer_item_code(args, item_bean): @@ -173,6 +175,6 @@ def apply_pos_settings(pos_settings, opts): out[fieldname] = pos_settings.get(fieldname) if out.get("warehouse"): - out["actual_qty"] = _get_available_qty(opts, out.get("warehouse")).get("actual_qty") + out["actual_qty"] = get_available_qty(opts.item_code, out.get("warehouse")).get("actual_qty") return out diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index df5e7614b4..e1b23f0c72 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -20,75 +20,36 @@ cur_frm.cscript.fname = "delivery_note_details"; cur_frm.cscript.other_fname = "other_charges"; cur_frm.cscript.sales_team_fname = "sales_team"; -wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/selling/doctype/sales_common/sales_common.js'); -// ONLOAD -// ================================================================================================ -cur_frm.cscript.onload = function(doc, dt, dn) { - cur_frm.cscript.manage_rounded_total(); - if(!doc.status) set_multiple(dt,dn,{status:'Draft'}); - if(!doc.transaction_date) set_multiple(dt,dn,{transaction_date:get_today()}); - if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()}); - if(doc.__islocal && doc.customer) cur_frm.cscript.customer(doc,dt,dn,onload=true); - if(!doc.price_list_currency) { - set_multiple(dt, dn, {price_list_currency: doc.currency, plc_conversion_rate:1}); - } +wn.provide("erpnext.stock"); +erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend({ + refresh: function(doc, dt, dn) { + this._super(); - if(doc.__islocal){ - hide_field(['customer_address', 'contact_person', 'customer_name', - 'address_display', 'contact_display', 'contact_mobile', - 'contact_email', 'territory', 'customer_group']); - } -} - -// REFRESH -// ================================================================================================ -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - cur_frm.clear_custom_buttons(); - erpnext.hide_naming_series(); + if(flt(doc.per_billed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']); - if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn); + if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Installation Note', cur_frm.cscript['Make Installation Note']); - - if(flt(doc.per_billed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']); + if (doc.docstatus==1) cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + + if(doc.docstatus==0 && !doc.__islocal) { + cur_frm.add_custom_button('Make Packing Slip', cur_frm.cscript['Make Packing Slip']); + } - if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Installation Note', cur_frm.cscript['Make Installation Note']); - - if (doc.docstatus==1) cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); - - if(doc.docstatus==0 && !doc.__islocal) { - cur_frm.add_custom_button('Make Packing Slip', cur_frm.cscript['Make Packing Slip']); + set_print_hide(doc, dt, dn); + + // unhide expense_account and cost_center is auto_inventory_accounting enabled + var aii_enabled = cint(sys_defaults.auto_inventory_accounting) + cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp("expense_account", aii_enabled); + cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp("cost_center", aii_enabled); } - - cur_frm.toggle_display("contact_info", doc.customer); - - set_print_hide(doc, cdt, cdn); - - // unhide expense_account and cost_center is auto_inventory_accounting enabled - var aii_enabled = cint(sys_defaults.auto_inventory_accounting) - cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp("expense_account", aii_enabled); - cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp("cost_center", aii_enabled); -} +}); - -//customer -cur_frm.cscript.customer = function(doc,dt,dn,onload) { - cur_frm.toggle_display("contact_info", doc.customer); - - var pl = doc.price_list_name; - var callback = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - if(doc.customer) - unhide_field(['customer_address','contact_person','territory','customer_group']); - cur_frm.refresh(); - if(!onload && (pl != doc.price_list_name)) cur_frm.cscript.price_list_name(doc, dt, dn); - } - var args = onload ? 'onload':'' - if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), - 'get_default_customer_shipping_address', args, callback); -} +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm})); cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); @@ -148,11 +109,6 @@ cur_frm.fields_dict['sales_order_no'].get_query = function(doc) { return repl('SELECT DISTINCT `tabSales Order`.`name` FROM `tabSales Order` WHERE `tabSales Order`.company = "%(company)s" and `tabSales Order`.`docstatus` = 1 and `tabSales Order`.`status` != "Stopped" and ifnull(`tabSales Order`.per_delivered,0) < 99.99 and %(cond)s `tabSales Order`.%(key)s LIKE "%s" ORDER BY `tabSales Order`.`name` DESC LIMIT 50', {company:doc.company,cond:cond}) } - -cur_frm.cscript.delivery_type = function(doc, cdt, cdn) { - if (doc.delivery_type = 'Sample') cfn_set_fields(doc, cdt, cdn); -} - cur_frm.cscript.serial_no = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; if (d.serial_no) { @@ -160,17 +116,6 @@ cur_frm.cscript.serial_no = function(doc, cdt, cdn) { } } - -cur_frm.cscript.warehouse = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - if (! d.item_code) {alert("please enter item code first"); return}; - if (d.warehouse) { - arg = "{'item_code':'" + d.item_code + "','warehouse':'" + d.warehouse +"'}"; - get_server_fields('get_actual_qty',arg,'delivery_note_details',doc,cdt,cdn,1); - } -} - - cur_frm.fields_dict['transporter_name'].get_query = function(doc) { return 'SELECT DISTINCT `tabSupplier`.`name` FROM `tabSupplier` WHERE `tabSupplier`.supplier_type = "transporter" AND `tabSupplier`.docstatus != 2 AND `tabSupplier`.%(key)s LIKE "%s" ORDER BY `tabSupplier`.`name` LIMIT 50'; } diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index 363fb399d5..950d687811 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -34,6 +34,9 @@ class DocType(SellingController): self.doclist = doclist self.tname = 'Delivery Note Item' self.fname = 'delivery_note_details' + + def set_customer_defaults(self): + self.get_default_customer_shipping_address() def validate_fiscal_year(self): get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date') @@ -78,25 +81,6 @@ class DocType(SellingController): def get_tc_details(self): return get_obj('Sales Common').get_tc_details(self) - def get_item_details(self, args=None): - import json - args = args and json.loads(args) or {} - if args.get('item_code'): - return get_obj('Sales Common').get_item_details(args, self) - else: - obj = get_obj('Sales Common') - for doc in self.doclist: - if doc.fields.get('item_code'): - arg = { - 'item_code':doc.fields.get('item_code'), - 'expense_account':doc.fields.get('expense_account'), - 'cost_center': doc.fields.get('cost_center'), - 'warehouse': doc.fields.get('warehouse')}; - ret = obj.get_item_defaults(arg) - for r in ret: - if not doc.fields.get(r): - doc.fields[r] = ret[r] - def get_barcode_details(self, barcode): return get_obj('Sales Common').get_barcode_details(barcode) @@ -105,12 +89,6 @@ class DocType(SellingController): """Re-calculates Basic Rate & amount based on Price List Selected""" get_obj('Sales Common').get_adj_percent(self) - - def get_actual_qty(self,args): - """Get Actual Qty of item in warehouse selected""" - return get_obj('Sales Common').get_available_qty(eval(args)) - - def get_rate(self,arg): return get_obj('Sales Common').get_rate(arg) diff --git a/stock/doctype/material_request/material_request.js b/stock/doctype/material_request/material_request.js index 3d4eed4842..881a764975 100644 --- a/stock/doctype/material_request/material_request.js +++ b/stock/doctype/material_request/material_request.js @@ -55,22 +55,7 @@ var new_cscript = new erpnext.buying.MaterialRequestController({frm: cur_frm}); // for backward compatibility: combine new and previous states $.extend(cur_frm.cscript, new_cscript); - -cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { - // second call - if(doc.__islocal){ - cur_frm.cscript.get_item_defaults(doc); - } -}; - -cur_frm.cscript.get_item_defaults = function(doc) { - var ch = getchildren( 'Material Request Item', doc.name, 'indent_details'); - if (flt(ch.length) > 0){ - $c_obj(make_doclist(doc.doctype, doc.name), 'get_item_defaults', '', function(r, rt) {refresh_field('indent_details'); }); - } -}; - cur_frm.cscript.qty = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; if (flt(d.qty) < flt(d.min_order_qty)) diff --git a/stock/doctype/purchase_receipt/purchase_receipt.js b/stock/doctype/purchase_receipt/purchase_receipt.js index 20740e76ab..f405a5501e 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/stock/doctype/purchase_receipt/purchase_receipt.js @@ -19,10 +19,11 @@ cur_frm.cscript.fname = "purchase_receipt_details"; cur_frm.cscript.other_fname = "purchase_tax_details"; wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); -wn.require('app/buying/doctype/purchase_common/purchase_common.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/buying/doctype/purchase_common/purchase_common.js'); -erpnext.buying.PurchaseReceiptController = erpnext.buying.BuyingController.extend({ +wn.provide("erpnext.stock"); +erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({ refresh: function() { this._super(); @@ -37,29 +38,49 @@ erpnext.buying.PurchaseReceiptController = erpnext.buying.BuyingController.exten if(wn.boot.control_panel.country == 'India') { unhide_field(['challan_no', 'challan_date']); } - }, + }, + + received_qty: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["qty", "received_qty"]); + + item.qty = (item.qty < item.received_qty) ? item.qty : item.received_qty; + this.qty(doc, cdt, cdn); + }, + + qty: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["qty", "received_qty"]); + + if(item.qty > item.received_qty) { + msgprint(wn._("Error") + ": " + wn._(wn.meta.get_label(item.doctype, "qty", item.name)) + + " > " + wn._(wn.meta.get_label(item.doctype, "received_qty", item.name))); + item.qty = item.rejected_qty = 0.0; + } else { + item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item)); + } + + this._super(); + }, + + rejected_qty: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["received_qty", "rejected_qty"]); + + if(item.rejected_qty > item.received_qty) { + msgprint(wn._("Error") + ": " + wn._(wn.meta.get_label(item.doctype, "rejected_qty", item.name)) + + " > " + wn._(wn.meta.get_label(item.doctype, "received_qty", item.name))); + item.qty = item.rejected_qty = 0.0; + } else { + item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item)); + } + + this.qty(doc, cdt, cdn); + }, }); -var new_cscript = new erpnext.buying.PurchaseReceiptController({frm: cur_frm}); - // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new_cscript); - -cur_frm.cscript.onload = function(doc, cdt, cdn) { - if(!doc.fiscal_year && doc.__islocal){ wn.model.set_default_values(doc);} - if (!doc.posting_date) doc.posting_date = dateutil.obj_to_str(new Date()); - if (!doc.transaction_date) doc.transaction_date = dateutil.obj_to_str(new Date()); - if (!doc.status) doc.status = 'Draft'; -} - -cur_frm.cscript.supplier = function(doc,dt,dn) { - if (doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }),'', doc, dt, dn, 1, function() { - cur_frm.refresh(); - }); - } -} +$.extend(cur_frm.cscript, new erpnext.stock.PurchaseReceiptController({frm: cur_frm})); cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.supplier) get_server_fields('get_supplier_address', JSON.stringify({supplier: doc.supplier, address: doc.supplier_address, contact: doc.contact_person}),'', doc, dt, dn, 1); @@ -137,81 +158,6 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) return 'SELECT `tabPrint Heading`.name FROM `tabPrint Heading` WHERE `tabPrint Heading`.docstatus !=2 AND `tabPrint Heading`.name LIKE "%s" ORDER BY `tabPrint Heading`.name ASC LIMIT 50'; } -//========================= Received Qty ============================================================= - -cur_frm.cscript.received_qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - ret = { - 'qty' : (flt(d.qty) && flt(d.qty) < flt(d.received_qty)) - ? flt(d.qty) : flt(d.received_qty), - 'stock_qty': 0, - 'rejected_qty' : 0, - } - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - cur_frm.cscript.calc_amount(doc, 2); -} - -//======================== Qty (Accepted Qty) ========================================================= - -cur_frm.cscript.qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - // Step 1 :=> Check If Qty > Received Qty - if (flt(d.qty) > flt(d.received_qty)) { - alert("Accepted Qty cannot be greater than Received Qty") - ret = { - 'qty' : 0, - 'stock_qty': 0, - 'rejected_qty' : 0 - } - // => Set Qty = 0 and rejected_qty = 0 - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - cur_frm.cscript.calc_amount(doc, 2); - // => Return - return - } - // Step 2 :=> Check IF Qty <= REceived Qty - else { - ret = { - 'rejected_qty':flt(d.received_qty) - flt(d.qty) - } - // => Set Rejected Qty = Received Qty - Qty - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - // => Calculate Amount - cur_frm.cscript.calc_amount(doc, 2); - cur_frm.cscript.update_stock_qty(doc,cdt,cdn); - } -} - -//======================== Rejected Qty ========================================================= -cur_frm.cscript.rejected_qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - // Step 1 :=> Check If Rejected Qty > Received Qty - if (flt(d.rejected_qty) > flt(d.received_qty)) { - alert("Rejected Qty cannot be greater than Received Qty") - ret = { - 'qty' : 0, - 'stock_qty': 0, - 'rejected_qty' : 0 - } - // => Set Qty = 0 and rejected_qty = 0 - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - cur_frm.cscript.calc_amount(doc, 2); - // => Return - return - } - // Step 2 :=> Check IF Rejected Qty <= REceived Qty - else { - ret = { - 'qty':flt(d.received_qty) - flt(d.rejected_qty) - } - // => Set Qty = Received Qty - Rejected Qty - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - // Calculate Amount - cur_frm.cscript.calc_amount(doc, 2); - cur_frm.cscript.update_stock_qty(doc,cdt,cdn); - } -} - //================================= Purchase Order No Get Query ==================================== cur_frm.fields_dict['purchase_order_no'].get_query = function(doc) { if (doc.supplier) diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index d4c61f5690..f1a06c9bdb 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -298,16 +298,27 @@ def validate_item_fetch(args, item): msgprint(_("Please specify Company"), raise_exception=True) def validate_currency(args, item, meta=None): + from webnotes.model.meta import get_field_precision if not meta: meta = webnotes.get_doctype(args.doctype) - + # validate conversion rate if meta.get_field("currency"): validate_conversion_rate(args.currency, args.conversion_rate, meta.get_label("conversion_rate"), args.company) + + # round it + args.conversion_rate = flt(args.conversion_rate, + get_field_precision(meta.get_field("conversion_rate"), args)) # validate price list conversion rate if meta.get_field("price_list_currency") and args.price_list_name and \ args.price_list_currency: validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate, - meta.get_label("plc_conversion_rate"), args.company) \ No newline at end of file + meta.get_label("plc_conversion_rate"), args.company) + + # round it + args.plc_conversion_rate = flt(args.plc_conversion_rate, + get_field_precision(meta.get_field("plc_conversion_rate"), args)) + + \ No newline at end of file From 923d41dfd200415699b68b46c57ccaf6866f5075 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 28 May 2013 17:23:36 +0530 Subject: [PATCH 16/17] [selling/buying] [fixes] fixes in client side code, server side code, print formats --- .../Sales Invoice Classic.txt | 7 +- .../Sales Invoice Modern.txt | 7 +- .../Sales Invoice Spartan.txt | 7 +- .../purchase_invoice/purchase_invoice.js | 19 ++--- .../purchase_invoice/purchase_invoice.py | 2 +- .../purchase_invoice/purchase_invoice.txt | 22 +++--- .../purchase_taxes_and_charges.txt | 36 +--------- .../doctype/sales_invoice/sales_invoice.js | 10 +-- .../doctype/sales_invoice/sales_invoice.txt | 20 ++++-- .../sales_taxes_and_charges.txt | 33 +-------- .../purchase_common/purchase_common.js | 55 +++++++------- .../doctype/purchase_order/purchase_order.txt | 24 ++++--- .../purchase_order_item.txt | 6 +- .../supplier_quotation/supplier_quotation.txt | 21 +++--- controllers/accounts_controller.py | 14 ++-- controllers/buying_controller.py | 8 ++- controllers/selling_controller.py | 30 +++++--- .../may_2013/p01_selling_net_total_export.py | 13 ---- public/js/transaction.js | 54 +++++++++++--- .../Quotation Classic/Quotation Classic.txt | 6 +- .../Quotation Modern/Quotation Modern.txt | 6 +- .../Quotation Spartan/Quotation Spartan.txt | 6 +- .../Sales Order Classic.txt | 7 +- .../Sales Order Modern/Sales Order Modern.txt | 7 +- .../Sales Order Spartan.txt | 7 +- selling/doctype/quotation/quotation.js | 12 ++++ selling/doctype/quotation/quotation.txt | 53 +++++++------- selling/doctype/sales_common/sales_common.js | 72 +++++++++---------- selling/doctype/sales_order/sales_order.txt | 22 +++--- .../Delivery Note Classic.txt | 7 +- .../Delivery Note Modern.txt | 7 +- .../Delivery Note Spartan.txt | 7 +- stock/doctype/delivery_note/delivery_note.txt | 20 ++++-- .../material_request/material_request.js | 14 ++-- .../purchase_receipt/purchase_receipt.txt | 24 ++++--- .../purchase_receipt_item.txt | 8 +-- utilities/transaction_base.py | 3 +- 37 files changed, 352 insertions(+), 324 deletions(-) diff --git a/accounts/Print Format/Sales Invoice Classic/Sales Invoice Classic.txt b/accounts/Print Format/Sales Invoice Classic/Sales Invoice Classic.txt index d72c847850..5aefa812f8 100644 --- a/accounts/Print Format/Sales Invoice Classic/Sales Invoice Classic.txt +++ b/accounts/Print Format/Sales Invoice Classic/Sales Invoice Classic.txt @@ -1,17 +1,18 @@ [ { - "creation": "2013-02-01 14:16:04", + "creation": "2013-04-19 13:30:27", "docstatus": 0, - "modified": "2013-02-26 11:11:20", + "modified": "2013-05-28 17:19:38", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Invoice", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Accounts", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/accounts/Print Format/Sales Invoice Modern/Sales Invoice Modern.txt b/accounts/Print Format/Sales Invoice Modern/Sales Invoice Modern.txt index 97c50fd6df..2f6251c07b 100644 --- a/accounts/Print Format/Sales Invoice Modern/Sales Invoice Modern.txt +++ b/accounts/Print Format/Sales Invoice Modern/Sales Invoice Modern.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-05-15 18:39:02", + "creation": "2013-04-19 13:30:27", "docstatus": 0, - "modified": "2013-02-26 11:10:58", + "modified": "2013-05-28 17:19:52", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Invoice", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Accounts", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/accounts/Print Format/Sales Invoice Spartan/Sales Invoice Spartan.txt b/accounts/Print Format/Sales Invoice Spartan/Sales Invoice Spartan.txt index 6be83d45d1..b00b3d85f5 100644 --- a/accounts/Print Format/Sales Invoice Spartan/Sales Invoice Spartan.txt +++ b/accounts/Print Format/Sales Invoice Spartan/Sales Invoice Spartan.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-05-15 18:39:02", + "creation": "2013-04-19 13:30:27", "docstatus": 0, - "modified": "2013-02-26 11:11:40", + "modified": "2013-05-28 17:19:22", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Invoice", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Accounts", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index b714a901c5..c6a787e50a 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -18,16 +18,11 @@ cur_frm.cscript.tname = "Purchase Invoice Item"; cur_frm.cscript.fname = "entries"; cur_frm.cscript.other_fname = "purchase_tax_details"; +wn.provide("erpnext.accounts"); wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); wn.require('app/buying/doctype/purchase_common/purchase_common.js'); -wn.provide("erpnext.accounts"); erpnext.accounts.PurchaseInvoiceController = erpnext.buying.BuyingController.extend({ - setup: function() { - this._super(); - - }, - onload: function() { this._super(); @@ -44,13 +39,13 @@ erpnext.accounts.PurchaseInvoiceController = erpnext.buying.BuyingController.ext // Show / Hide button if(doc.docstatus==1 && doc.outstanding_amount > 0) - cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher); + this.frm.add_custom_button('Make Payment Entry', this.make_bank_voucher); if(doc.docstatus==1) { - cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry); + this.frm.add_custom_button('View Ledger', this.view_ledger_entry); } - cur_frm.cscript.is_opening(doc); + this.is_opening(doc); }, credit_to: function() { @@ -63,13 +58,13 @@ erpnext.accounts.PurchaseInvoiceController = erpnext.buying.BuyingController.ext }, allocated_amount: function() { - this.calculate_total_advance(); + this.calculate_total_advance("Purchase Invoice", "advance_allocation_details"); this.frm.refresh_fields(); - }, + } }); // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new erpnext.buying.PurchaseInvoiceController({frm: cur_frm})); +$.extend(cur_frm.cscript, new erpnext.accounts.PurchaseInvoiceController({frm: cur_frm})); cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index 7e18041c8d..59e66b57db 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -92,7 +92,7 @@ class DocType(BuyingController): return ret def set_supplier_defaults(self): - # TODO cleanup these methods + self.doc.fields.update(self.get_cust()) self.doc.fields.update(self.get_credit_to()) super(DocType, self).set_supplier_defaults() diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.txt b/accounts/doctype/purchase_invoice/purchase_invoice.txt index 283c612371..4647fd235d 100755 --- a/accounts/doctype/purchase_invoice/purchase_invoice.txt +++ b/accounts/doctype/purchase_invoice/purchase_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-07 13:50:30", + "creation": "2013-05-21 16:16:39", "docstatus": 0, - "modified": "2013-05-13 11:12:56", + "modified": "2013-05-28 12:18:35", "modified_by": "Administrator", "owner": "Administrator" }, @@ -89,10 +89,11 @@ "read_only": 0 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "supplier_name", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Name", "oldfieldname": "supplier_name", @@ -100,34 +101,38 @@ "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -392,6 +397,7 @@ "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -399,7 +405,6 @@ "read_only": 0 }, { - "depends_on": "eval:doc.supplier", "doctype": "DocField", "fieldname": "supplier_address", "fieldtype": "Link", @@ -415,7 +420,6 @@ "width": "50%" }, { - "depends_on": "eval:doc.supplier", "doctype": "DocField", "fieldname": "contact_person", "fieldtype": "Link", diff --git a/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt b/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt index 619aed1954..d6d2a92b7a 100644 --- a/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt +++ b/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-19 11:00:06", + "creation": "2013-05-21 16:16:04", "docstatus": 0, - "modified": "2013-05-07 11:23:56", + "modified": "2013-05-28 12:02:02", "modified_by": "Administrator", "owner": "Administrator" }, @@ -101,7 +101,7 @@ "oldfieldname": "tax_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 0, + "read_only": 1, "reqd": 0 }, { @@ -159,35 +159,5 @@ "print_hide": 1, "read_only": 0, "search_index": 0 - }, - { - "description": "Cheating Field\nPlease do not delete ", - "doctype": "DocField", - "fieldname": "total_tax_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Total +Tax", - "no_copy": 1, - "oldfieldname": "total_tax_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 0, - "report_hide": 1 - }, - { - "description": "Cheating Field\nPlease do not delete ", - "doctype": "DocField", - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Tax Amount", - "no_copy": 1, - "oldfieldname": "total_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 0, - "report_hide": 1 } ] \ No newline at end of file diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index 10853ea1f6..105d350bb2 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -79,23 +79,19 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte }, allocated_amount: function() { - this.calculate_total_advance(); + this.calculate_total_advance("Sales Invoice", "advance_adjustment_details"); this.frm.refresh_fields(); }, write_off_outstanding_amount_automatically: function() { if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { wn.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); + // this will make outstanding amount 0 this.frm.set_value("write_off_amount", flt(this.frm.doc.grand_total - this.frm.doc.paid_amount), precision("write_off_amount")); } this.frm.runclientscript("write_off_amount"); - - // TODO doubt? - // if write off amount = grand total - paid amount - // then why is outstanding amount = grand total - write off amount - paid amount - advance - // when write off amount already is grand total - paid amount! }, write_off_amount: function() { @@ -137,8 +133,6 @@ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) { for(f in item_flds_pos) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_pos[f], false); } - cur_frm.toggle_display("contact_section", doc.customer); - // India related fields var cp = wn.control_panel; if (cp.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']); diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt index 8b114e7ffb..8390be92a1 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.txt +++ b/accounts/doctype/sales_invoice/sales_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-21 16:16:41", + "creation": "2013-05-24 19:29:05", "docstatus": 0, - "modified": "2013-05-21 18:25:07", + "modified": "2013-05-28 12:43:23", "modified_by": "Administrator", "owner": "Administrator" }, @@ -120,10 +120,11 @@ "read_only": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Name", "oldfieldname": "customer_name", @@ -131,34 +132,38 @@ "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -748,6 +753,7 @@ "read_only": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", diff --git a/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt b/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt index 55a41a9345..ba9f90724a 100644 --- a/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt +++ b/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:41", + "creation": "2013-04-24 11:39:32", "docstatus": 0, - "modified": "2013-04-17 14:05:50", + "modified": "2013-05-28 11:59:02", "modified_by": "Administrator", "owner": "Administrator" }, @@ -85,6 +85,7 @@ "oldfieldname": "tax_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", + "read_only": 1, "reqd": 0 }, { @@ -128,34 +129,6 @@ "print_hide": 1, "search_index": 1 }, - { - "description": "Cheating Field\nPlease do not delete ", - "doctype": "DocField", - "fieldname": "total_tax_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Total Tax Amount", - "no_copy": 1, - "oldfieldname": "total_tax_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "report_hide": 1 - }, - { - "description": "Cheating Field\nPlease do not delete ", - "doctype": "DocField", - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Total Amount", - "no_copy": 1, - "oldfieldname": "total_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "report_hide": 1 - }, { "allow_on_submit": 0, "description": "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount", diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index 65b44c3d8c..f0f38671f5 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -53,10 +53,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ }); }, - validate: function() { - this.calculate_taxes_and_totals(); - }, - supplier: function() { if(this.frm.doc.supplier || this.frm.doc.credit_to) { if(!this.frm.doc.company) { @@ -79,7 +75,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ }); } } - } + }, item_code: function(doc, cdt, cdn) { var me = this; @@ -133,7 +129,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ }, discount_rate: function(doc, cdt, cdn) { - this.import_rate(doc, cdt, cdn); + this.import_ref_rate(doc, cdt, cdn); }, import_rate: function(doc, cdt, cdn) { @@ -159,7 +155,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ child: item, args: { item_code: item.item_code, - uom: item.uom, + uom: item.uom }, callback: function(r) { if(!r.exc) { @@ -178,7 +174,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ conversion_factor: function(doc, cdt, cdn) { if(wn.meta.get_docfield(cdt, "stock_qty", cdn)) { var item = wn.model.get_doc(cdt, cdn); - wn.model.round_floats_in(item, ["qty", "conversion_factor"]) + wn.model.round_floats_in(item, ["qty", "conversion_factor"]); item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); refresh_field("stock_qty", item.name, item.parentfield); } @@ -192,7 +188,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ child: item, args: { item_code: item.item_code, - warehouse: item.warehouse, + warehouse: item.warehouse } }); } @@ -211,9 +207,16 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } }, + category: function(doc, cdt, cdn) { + // should be the category field of tax table + if(cdt != doc.doctype) { + this.calculate_taxes_and_totals(); + } + }, + calculate_taxes_and_totals: function() { this._super(); - this.calculate_total_advance(); + this.calculate_total_advance("Purchase Invoice", "advance_allocation_details"); this.frm.refresh_fields(); }, @@ -305,10 +308,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } }, - calculate_total_advance: function() { - this._super("Purchase Invoice", "advance_allocation_details"); - }, - calculate_outstanding_amount: function() { if(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.docstatus < 2) { wn.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]); @@ -331,12 +330,10 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ }, show_item_wise_taxes: function() { - $(this.get_item_wise_taxes_html()) - .appendTo($(this.frm.fields_dict.tax_calculation.wrapper).empty()); - }, - - recalculate: function() { - this.calculate_taxes_and_totals(); + if(this.frm.fields_dict.tax_calculation) { + $(this.get_item_wise_taxes_html()) + .appendTo($(this.frm.fields_dict.tax_calculation.wrapper).empty()); + } }, set_dynamic_labels: function(doc, dt, dn) { @@ -354,7 +351,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ $.each(fields_list, function(i, fname) { var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname); if(docfield) { - var label = wn._((docfield.label || "")).replace(/\([^\)]*\)/g, ""); + var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); field_label_map[fname] = label.trim() + " (" + currency + ")"; } }); @@ -383,7 +380,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ this.frm.doc.currency != company_currency); this.frm.toggle_display(["plc_conversion_rate"], - this.frm.price_list_currency != company_currency) + this.frm.price_list_currency != company_currency); // set labels $.each(field_label_map, function(fname, label) { @@ -400,12 +397,12 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ $.each(fields_list, function(i, fname) { var docfield = wn.meta.get_docfield(grid_doctype, fname); if(docfield) { - var label = wn._((docfield.label || "")).replace(/\([^\)]*\)/g, ""); + var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); field_label_map[grid_doctype + "-" + fname] = label.trim() + " (" + currency + ")"; } }); - } + }; setup_field_label_map(["purchase_rate", "purchase_ref_rate", "amount", "rate"], company_currency, this.fname); @@ -413,7 +410,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ setup_field_label_map(["import_rate", "import_ref_rate", "import_amount"], this.frm.doc.currency, this.fname); - setup_field_label_map(["tax_amount", "total"], company_currency, this.other_fname); + if(this.frm.fields_dict[this.other_fname]) { + setup_field_label_map(["tax_amount", "total"], company_currency, this.other_fname); + } if(this.frm.fields_dict["advance_allocation_details"]) { setup_field_label_map(["advance_amount", "allocated_amount"], company_currency, @@ -433,9 +432,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ $.each(field_label_map, function(fname, label) { $wrapper.find('[data-grid-fieldname="'+fname+'"]').text(label); }); - }, - - + } }); // to save previous state of cur_frm.cscript @@ -449,4 +446,4 @@ $.extend(cur_frm.cscript, prev_cscript); var tname = cur_frm.cscript.tname; -var fname = cur_frm.cscript.fname; +var fname = cur_frm.cscript.fname; \ No newline at end of file diff --git a/buying/doctype/purchase_order/purchase_order.txt b/buying/doctype/purchase_order/purchase_order.txt index 8a56f26e9c..902329b050 100644 --- a/buying/doctype/purchase_order/purchase_order.txt +++ b/buying/doctype/purchase_order/purchase_order.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-25 16:01:24", + "creation": "2013-05-21 16:16:39", "docstatus": 0, - "modified": "2013-02-18 13:37:11", + "modified": "2013-05-28 12:20:33", "modified_by": "Administrator", "owner": "Administrator" }, @@ -65,43 +65,48 @@ "search_index": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "supplier_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Name", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -554,6 +559,7 @@ "oldfieldtype": "Text Editor" }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -808,7 +814,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material User", @@ -831,7 +836,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Purchase Manager", @@ -854,7 +858,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "permlevel": 0, "report": 1, "role": "Purchase User", @@ -866,7 +869,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "All", "submit": 0 diff --git a/buying/doctype/purchase_order_item/purchase_order_item.txt b/buying/doctype/purchase_order_item/purchase_order_item.txt index 01a144a143..5d69fb057d 100755 --- a/buying/doctype/purchase_order_item/purchase_order_item.txt +++ b/buying/doctype/purchase_order_item/purchase_order_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-07 11:42:55", + "creation": "2013-05-24 19:29:06", "docstatus": 0, - "modified": "2013-05-22 11:59:52", + "modified": "2013-05-28 12:13:21", "modified_by": "Administrator", "owner": "Administrator" }, @@ -361,7 +361,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "width": "100px" }, { diff --git a/buying/doctype/supplier_quotation/supplier_quotation.txt b/buying/doctype/supplier_quotation/supplier_quotation.txt index dace56a43f..37e0e3c01c 100644 --- a/buying/doctype/supplier_quotation/supplier_quotation.txt +++ b/buying/doctype/supplier_quotation/supplier_quotation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-25 16:01:25", + "creation": "2013-05-21 16:16:45", "docstatus": 0, - "modified": "2013-02-18 13:40:17", + "modified": "2013-05-28 12:19:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -65,43 +65,48 @@ "search_index": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "supplier_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Name", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -520,6 +525,7 @@ "oldfieldtype": "Text Editor" }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -723,7 +729,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "All", diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index 1499e0a306..d7c9441e8c 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -32,7 +32,7 @@ class AccountsController(TransactionBase): validate_conversion_rate(self.doc.currency, self.doc.conversion_rate, self.meta.get_label("conversion_rate"), self.doc.company) - # self.calculate_taxes_and_totals() + self.calculate_taxes_and_totals() self.validate_value("grand_total", ">=", 0) self.set_total_in_words() @@ -84,9 +84,6 @@ class AccountsController(TransactionBase): def calculate_taxes_and_totals(self): self.doc.conversion_rate = flt(self.doc.conversion_rate) - - # TODO validate conversion rate - self.item_doclist = self.doclist.get({"parentfield": self.fname}) self.tax_doclist = self.doclist.get({"parentfield": self.other_fname}) @@ -264,6 +261,15 @@ class AccountsController(TransactionBase): item.fields[base_field] = flt((flt(item.fields[print_field], self.precision(print_field, item)) * self.doc.conversion_rate), self.precision(base_field, item)) + + def calculate_total_advance(self, parenttype, advance_parentfield): + if self.doc.doctype == parenttype and self.doc.docstatus < 2: + sum_of_allocated_amount = sum([flt(adv.allocated_amount, self.precision("allocated_amount", adv)) + for adv in self.doclist.get({"parentfield": advance_parentfield})]) + + self.doc.total_advance = flt(sum_of_allocated_amount, self.precision("total_advance")) + + self.calculate_outstanding_amount() def get_gl_dict(self, args, cancel=None): """this method populates the common properties of a gl entry record""" diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 8313b5409d..303eef3894 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -50,7 +50,7 @@ class BuyingController(StockController): self.set_missing_item_details(get_item_details) def set_supplier_defaults(self): - self.get_default_supplier_address() + self.get_default_supplier_address(self.doc.fields) def get_purchase_tax_details(self): self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details") @@ -93,7 +93,7 @@ class BuyingController(StockController): def calculate_taxes_and_totals(self): self.other_fname = "purchase_tax_details" super(BuyingController, self).calculate_taxes_and_totals() - self.calculate_outstanding_amount() + self.calculate_total_advance("Purchase Invoice", "advance_allocation_details") def calculate_item_values(self): # hack! - cleaned up in _cleanup() @@ -168,6 +168,10 @@ class BuyingController(StockController): item.purchase_rate = item.rate del item.fields["rate"] + if not self.meta.get_field("item_tax_amount", parentfield=self.fname): + for item in self.item_doclist: + del item.fields["item_tax_amount"] + def set_item_tax_amount(self, item, tax, current_tax_amount): """ item_tax_amount is the total tax amount applied on that item diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index d5399a4833..3adc639271 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -102,9 +102,9 @@ class SellingController(StockController): super(SellingController, self).calculate_taxes_and_totals() + self.calculate_total_advance("Sales Invoice", "advance_adjustment_details") self.calculate_commission() self.calculate_contribution() - # self.calculate_outstanding_amount() def determine_exclusive_rate(self): if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)): @@ -201,15 +201,27 @@ class SellingController(StockController): self.doc.rounded_total = round(self.doc.grand_total) self.doc.rounded_total_export = round(self.doc.grand_total_export) - - def calculate_commission(self): - self.round_floats_in(self.doc, ["net_total", "commission_rate"]) - if self.doc.commission_rate > 100.0: - msgprint(_(self.meta.get_label("commission_rate")) + " " + - _("cannot be greater than 100"), raise_exception=True) - self.doc.total_commission = flt(self.doc.net_total * self.doc.commission_rate / 100.0, - self.precision("total_commission")) + def calculate_outstanding_amount(self): + # NOTE: + # write_off_amount is only for POS Invoice + # total_advance is only for non POS Invoice + if self.doc.doctype == "Sales Invoice" and self.doc.docstatus < 2: + self.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount", + "paid_amount"]) + total_amount_to_pay = self.doc.grand_total - self.doc.write_off_amount + self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount, + self.precision("outstanding_amount")) + + def calculate_commission(self): + if self.meta.get_field("commission_rate"): + self.round_floats_in(self.doc, ["net_total", "commission_rate"]) + if self.doc.commission_rate > 100.0: + msgprint(_(self.meta.get_label("commission_rate")) + " " + + _("cannot be greater than 100"), raise_exception=True) + + self.doc.total_commission = flt(self.doc.net_total * self.doc.commission_rate / 100.0, + self.precision("total_commission")) def calculate_contribution(self): total = 0.0 diff --git a/patches/may_2013/p01_selling_net_total_export.py b/patches/may_2013/p01_selling_net_total_export.py index 7d4e3463b9..eb6da234ab 100644 --- a/patches/may_2013/p01_selling_net_total_export.py +++ b/patches/may_2013/p01_selling_net_total_export.py @@ -13,17 +13,4 @@ def execute(): for module, doctype in (("Accounts", "Sales Invoice Item"), ("Selling", "Sales Order Item"), ("Selling", "Quotation Item"), ("Stock", "Delivery Note Item")): - if cint(webnotes.conn.get_value("DocField", {"parent": doctype, "fieldname": "ref_rate"}, "read_only")) == 0 and \ - not webnotes.conn.sql("""select name from `tabProperty Setter` where doc_type=%s and doctype_or_field='DocField' - and field_name='ref_rate' and property='read_only'""", doctype): - webnotes.bean({ - "doctype": "Property Setter", - "doc_type": doctype, - "doctype_or_field": "DocField", - "field_name": "ref_rate", - "property": "read_only", - "property_type": "Check", - "value": "0" - }).insert() - webnotes.reload_doc(module, "DocType", doctype) \ No newline at end of file diff --git a/public/js/transaction.js b/public/js/transaction.js index 86fe69a327..1fe412110e 100644 --- a/public/js/transaction.js +++ b/public/js/transaction.js @@ -46,9 +46,7 @@ erpnext.TransactionController = wn.ui.form.Controller.extend({ this.frm.clear_custom_buttons(); erpnext.hide_naming_series(); this.show_item_wise_taxes(); - - if(this.frm.fields_dict.currency) - this.currency(); + this.frm.fields_dict.currency ? this.currency() : this.set_dynamic_labels(); }, onload_post_render: function() { @@ -67,6 +65,10 @@ erpnext.TransactionController = wn.ui.form.Controller.extend({ } }, + validate: function() { + this.calculate_taxes_and_totals(); + }, + company: function() { if(this.frm.doc.company) { var me = this; @@ -132,6 +134,30 @@ erpnext.TransactionController = wn.ui.form.Controller.extend({ this.calculate_taxes_and_totals(); }, + tax_rate: function(doc, cdt, cdn) { + this.calculate_taxes_and_totals(); + }, + + row_id: function(doc, cdt, cdn) { + var tax = wn.model.get_doc(cdt, cdn); + try { + this.validate_on_previous_row(tax); + this.calculate_taxes_and_totals(); + } catch(e) { + tax.row_id = null; + refresh_field("row_id", tax.name, tax.parentfield); + throw e; + } + }, + + recalculate: function() { + this.calculate_taxes_and_totals(); + }, + + recalculate_values: function() { + this.calculate_taxes_and_totals(); + }, + included_in_print_rate: function(doc, cdt, cdn) { var tax = wn.model.get_doc(cdt, cdn); try { @@ -267,14 +293,25 @@ erpnext.TransactionController = wn.ui.form.Controller.extend({ }); }, + _validate_before_fetch: function(fieldname) { + var me = this; + if(!me.frm.doc[fieldname]) { + return (wn._("Please specify") + ": " + + wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + ". " + wn._("It is needed to fetch Item Details.")); + } + return null; + }, + validate_company_and_party: function(party_field) { + var me = this; var valid = true; + var msg = ""; $.each(["company", party_field], function(i, fieldname) { - if(!me.frm.doc[fieldname]) { + var msg_for_fieldname = me._validate_before_fetch(fieldname); + if(msg_for_fieldname) { + msgprint(msg_for_fieldname); valid = false; - msgprint(wn._("Please specify") + ": " + - wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + - ". " + wn._("It is needed to fetch Item Details.")); } }); return valid; @@ -300,7 +337,7 @@ erpnext.TransactionController = wn.ui.form.Controller.extend({ } var company_currency = this.get_company_currency(); - var valid_conversion_rate = conversion_rate ? + var valid_conversion_rate = this.frm.doc.conversion_rate ? ((this.frm.doc.currency == company_currency && this.frm.doc.conversion_rate == 1.0) || (this.frm.doc.currency != company_currency && this.frm.doc.conversion_rate != 1.0)) : false; @@ -323,6 +360,7 @@ erpnext.TransactionController = wn.ui.form.Controller.extend({ this.calculate_taxes(); this.calculate_totals(); this._cleanup(); + this.show_item_wise_taxes(); }, diff --git a/selling/Print Format/Quotation Classic/Quotation Classic.txt b/selling/Print Format/Quotation Classic/Quotation Classic.txt index 03a056b18c..0a63309354 100644 --- a/selling/Print Format/Quotation Classic/Quotation Classic.txt +++ b/selling/Print Format/Quotation Classic/Quotation Classic.txt @@ -1,15 +1,15 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-04-02 18:14:21", + "modified": "2013-05-28 17:17:05", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Quotation", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", "print_format_type": "Client", diff --git a/selling/Print Format/Quotation Modern/Quotation Modern.txt b/selling/Print Format/Quotation Modern/Quotation Modern.txt index b7a29bf4c5..0f841aa94d 100644 --- a/selling/Print Format/Quotation Modern/Quotation Modern.txt +++ b/selling/Print Format/Quotation Modern/Quotation Modern.txt @@ -1,15 +1,15 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-04-02 18:14:05", + "modified": "2013-05-28 17:18:02", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Quotation", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", "print_format_type": "Client", diff --git a/selling/Print Format/Quotation Spartan/Quotation Spartan.txt b/selling/Print Format/Quotation Spartan/Quotation Spartan.txt index 3fbf6291c3..d2bfcd0737 100644 --- a/selling/Print Format/Quotation Spartan/Quotation Spartan.txt +++ b/selling/Print Format/Quotation Spartan/Quotation Spartan.txt @@ -1,15 +1,15 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-04-02 18:13:47", + "modified": "2013-05-28 17:18:38", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Quotation", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", "print_format_type": "Client", diff --git a/selling/Print Format/Sales Order Classic/Sales Order Classic.txt b/selling/Print Format/Sales Order Classic/Sales Order Classic.txt index 9415035aaf..28e3af07eb 100644 --- a/selling/Print Format/Sales Order Classic/Sales Order Classic.txt +++ b/selling/Print Format/Sales Order Classic/Sales Order Classic.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-01-25 17:18:37", + "modified": "2013-05-28 17:20:59", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Order", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/selling/Print Format/Sales Order Modern/Sales Order Modern.txt b/selling/Print Format/Sales Order Modern/Sales Order Modern.txt index f222d5bad2..5237068e40 100644 --- a/selling/Print Format/Sales Order Modern/Sales Order Modern.txt +++ b/selling/Print Format/Sales Order Modern/Sales Order Modern.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-01-25 17:18:20", + "modified": "2013-05-28 17:21:05", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Order", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/selling/Print Format/Sales Order Spartan/Sales Order Spartan.txt b/selling/Print Format/Sales Order Spartan/Sales Order Spartan.txt index 3db5f05f09..f1f5d921ca 100644 --- a/selling/Print Format/Sales Order Spartan/Sales Order Spartan.txt +++ b/selling/Print Format/Sales Order Spartan/Sales Order Spartan.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-01-25 17:19:08", + "modified": "2013-05-28 17:20:50", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Order", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js index db45672b5c..9de9b1686c 100644 --- a/selling/doctype/quotation/quotation.js +++ b/selling/doctype/quotation/quotation.js @@ -51,6 +51,18 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ this.frm.toggle_reqd("lead", this.frm.doc.quotation_to == "Lead"); this.frm.toggle_reqd("customer", this.frm.doc.quotation_to == "Customer"); }, + + validate_company_and_party: function(party_field) { + if(this.frm.doc.quotation_to == "Lead") { + return true; + } else if(!this.frm.doc.quotation_to) { + msgprint(wn._("Please select a value for" + " " + wn.meta.get_label(this.frm.doc.doctype, + "quotation_to", this.frm.doc.name))); + return false; + } else { + return this._super(party_field); + } + }, }); // for backward compatibility: combine new and previous states diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt index 3ea87e198a..322be88b6e 100644 --- a/selling/doctype/quotation/quotation.txt +++ b/selling/doctype/quotation/quotation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-22 12:10:46", + "creation": "2013-05-24 19:29:08", "docstatus": 0, - "modified": "2013-05-22 16:55:07", + "modified": "2013-05-28 14:50:59", "modified_by": "Administrator", "owner": "Administrator" }, @@ -78,6 +78,7 @@ "reqd": 1 }, { + "depends_on": "eval:doc.quotation_to == \"Customer\"", "doctype": "DocField", "fieldname": "customer", "fieldtype": "Link", @@ -92,6 +93,7 @@ "search_index": 1 }, { + "depends_on": "eval:doc.quotation_to == \"Lead\"", "doctype": "DocField", "fieldname": "lead", "fieldtype": "Link", @@ -105,19 +107,21 @@ "read_only": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Customer Name", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "in_filter": 0, "label": "Address", "oldfieldname": "customer_address", @@ -128,29 +132,32 @@ "search_index": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "in_filter": 0, "label": "Contact", "print_hide": 0, "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "print_hide": 0, "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -614,6 +621,7 @@ "read_only": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "col_break98", "fieldtype": "Column Break", @@ -667,6 +675,7 @@ "search_index": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_group", "fieldtype": "Link", @@ -837,19 +846,6 @@ "read_only": 1, "width": "150px" }, - { - "description": "The date at which current entry is corrected in the system.", - "doctype": "DocField", - "fieldname": "amendment_date", - "fieldtype": "Date", - "label": "Amendment Date", - "no_copy": 1, - "oldfieldname": "amendment_date", - "oldfieldtype": "Date", - "print_hide": 1, - "read_only": 0, - "width": "100px" - }, { "doctype": "DocField", "fieldname": "communication_history", @@ -886,23 +882,28 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "role": "Sales User", + "role": "Sales Manager", "submit": 1, "write": 1 }, - { - "doctype": "DocPerm", - "role": "Customer" - }, { "amend": 1, "cancel": 1, "create": 1, "doctype": "DocPerm", - "role": "Sales Manager", + "role": "Sales User", "submit": 1, "write": 1 }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "role": "Customer", + "submit": 0, + "write": 0 + }, { "amend": 1, "cancel": 1, diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js index 828d3d3fb8..0b0b3290d7 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -29,29 +29,19 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate"); }, - // 1 onload: function() { this._super(); this.toggle_rounded_total(); - - // TODO set depends_on for customer related fields - }, - - validate: function() { - this.calculate_taxes_and_totals(); - - // TODO calc adjustment amount }, customer: function() { + var me = this; if(this.frm.doc.customer || this.frm.doc.debit_to) { if(!this.frm.doc.company) { this.frm.set_value("customer", null); msgprint(wn._("Please specify Company")); } else { - var me = this; var price_list_name = this.frm.doc.price_list_name; - this.frm.call({ doc: this.frm.doc, method: "set_customer_defaults", @@ -59,14 +49,14 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ callback: function(r) { if(!r.exc) { me.frm.refresh_fields(); - if(me.frm.doc.price_list_name !== price_list_name) me.price_list_name(); + (me.frm.doc.price_list_name !== price_list_name) ? + me.price_list_name() : + me.price_list_currency(); } } }); } } - - // TODO hide/unhide related fields }, barcode: function(doc, cdt, cdn) { @@ -127,7 +117,12 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }, adj_rate: function(doc, cdt, cdn) { - this.ref_rate(doc, cdt, cdn); + var item = wn.model.get_doc(cdt, cdn); + if(!item.ref_rate) { + item.adj_rate = 0.0; + } else { + this.ref_rate(doc, cdt, cdn); + } }, export_rate: function(doc, cdt, cdn) { @@ -208,14 +203,11 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ calculate_taxes_and_totals: function() { this._super(); - this.calculate_total_advance(); + this.calculate_total_advance("Sales Invoice", "advance_adjustment_details"); this.calculate_commission(); this.calculate_contribution(); - - // TODO - // outstanding amount - - // check for custom_recalc in custom scripts of server + + // TODO check for custom_recalc in custom scripts of server this.frm.refresh_fields(); }, @@ -325,14 +317,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ this.frm.doc.rounded_total_export = Math.round(this.frm.doc.grand_total_export); }, - calculate_total_advance: function() { - this._super("Sales Invoice", "advance_adjustment_details"); - }, - calculate_outstanding_amount: function() { - // TODO - I find this incorrect! - // see TODO of sales invoice.js / write_off_outstanding_amount_automatically - + // NOTE: + // write_off_amount is only for POS Invoice + // total_advance is only for non POS Invoice if(this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.docstatus < 2) { wn.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount", "paid_amount"]); @@ -343,15 +331,17 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }, calculate_commission: function() { - if(this.frm.doc.commission_rate > 100) { - var msg = wn._(wn.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) + - " " + wn._("cannot be greater than 100"); - msgprint(msg); - throw msg; - } + if(this.frm.fields_dict.commission_rate) { + if(this.frm.doc.commission_rate > 100) { + var msg = wn._(wn.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) + + " " + wn._("cannot be greater than 100"); + msgprint(msg); + throw msg; + } - this.frm.doc.total_commission = flt(this.frm.doc.net_total * this.frm.doc.commission_rate / 100.0, - precision("total_commission")); + this.frm.doc.total_commission = flt(this.frm.doc.net_total * this.frm.doc.commission_rate / 100.0, + precision("total_commission")); + } }, calculate_contribution: function() { @@ -373,8 +363,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }, show_item_wise_taxes: function() { - $(this.get_item_wise_taxes_html()) - .appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty()); + if(this.frm.fields_dict.other_charges_calculation) { + $(this.get_item_wise_taxes_html()) + .appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty()); + } }, get_charges: function() { @@ -407,7 +399,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ $.each(fields_list, function(i, fname) { var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname); if(docfield) { - var label = wn._((docfield.label || "")).replace(/\([^\)]*\)/g, ""); + var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); field_label_map[fname] = label.trim() + " (" + currency + ")"; } }); @@ -452,7 +444,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ $.each(fields_list, function(i, fname) { var docfield = wn.meta.get_docfield(grid_doctype, fname); if(docfield) { - var label = wn._((docfield.label || "")).replace(/\([^\)]*\)/g, ""); + var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); field_label_map[grid_doctype + "-" + fname] = label.trim() + " (" + currency + ")"; } diff --git a/selling/doctype/sales_order/sales_order.txt b/selling/doctype/sales_order/sales_order.txt index fdf60a21ec..fb5848f8c3 100644 --- a/selling/doctype/sales_order/sales_order.txt +++ b/selling/doctype/sales_order/sales_order.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-21 16:16:41", + "creation": "2013-05-24 19:29:08", "docstatus": 0, - "modified": "2013-05-21 18:30:14", + "modified": "2013-05-28 15:05:38", "modified_by": "Administrator", "owner": "Administrator" }, @@ -76,42 +76,47 @@ "search_index": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "label": "Name", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -448,7 +453,7 @@ "fieldname": "other_charges_total_export", "fieldtype": "Currency", "label": "Taxes and Charges Total (Export)", - "options": "company", + "options": "currency", "print_hide": 1, "read_only": 1 }, @@ -596,6 +601,7 @@ "print_hide": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_info", "fieldtype": "Section Break", diff --git a/stock/Print Format/Delivery Note Classic/Delivery Note Classic.txt b/stock/Print Format/Delivery Note Classic/Delivery Note Classic.txt index c5beb65b0d..4f6272d773 100644 --- a/stock/Print Format/Delivery Note Classic/Delivery Note Classic.txt +++ b/stock/Print Format/Delivery Note Classic/Delivery Note Classic.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:31:11", "docstatus": 0, - "modified": "2013-01-25 17:19:46", + "modified": "2013-05-28 17:20:31", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Delivery Note", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Stock", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/stock/Print Format/Delivery Note Modern/Delivery Note Modern.txt b/stock/Print Format/Delivery Note Modern/Delivery Note Modern.txt index 6993bfdce0..82d2376d04 100644 --- a/stock/Print Format/Delivery Note Modern/Delivery Note Modern.txt +++ b/stock/Print Format/Delivery Note Modern/Delivery Note Modern.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:31:11", "docstatus": 0, - "modified": "2013-01-25 17:20:58", + "modified": "2013-05-28 17:20:21", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Delivery Note", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Stock", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/stock/Print Format/Delivery Note Spartan/Delivery Note Spartan.txt b/stock/Print Format/Delivery Note Spartan/Delivery Note Spartan.txt index 69eb9ff66f..81ecbd29b5 100644 --- a/stock/Print Format/Delivery Note Spartan/Delivery Note Spartan.txt +++ b/stock/Print Format/Delivery Note Spartan/Delivery Note Spartan.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:31:11", "docstatus": 0, - "modified": "2013-01-25 17:19:30", + "modified": "2013-05-28 17:20:39", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Delivery Note", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Stock", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/stock/doctype/delivery_note/delivery_note.txt b/stock/doctype/delivery_note/delivery_note.txt index 72c6f47188..4504db047a 100644 --- a/stock/doctype/delivery_note/delivery_note.txt +++ b/stock/doctype/delivery_note/delivery_note.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-21 16:16:31", + "creation": "2013-05-24 19:29:09", "docstatus": 0, - "modified": "2013-05-21 18:30:32", + "modified": "2013-05-28 12:26:04", "modified_by": "Administrator", "owner": "Administrator" }, @@ -76,10 +76,11 @@ "search_index": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Customer Name", "read_only": 1 @@ -96,34 +97,38 @@ "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Shipping Address", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -690,6 +695,7 @@ "width": "100px" }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_info", "fieldtype": "Section Break", diff --git a/stock/doctype/material_request/material_request.js b/stock/doctype/material_request/material_request.js index 881a764975..6c7b2a7b88 100644 --- a/stock/doctype/material_request/material_request.js +++ b/stock/doctype/material_request/material_request.js @@ -17,8 +17,8 @@ cur_frm.cscript.tname = "Material Request Item"; cur_frm.cscript.fname = "indent_details"; -wn.require('app/buying/doctype/purchase_common/purchase_common.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/buying/doctype/purchase_common/purchase_common.js'); erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.extend({ refresh: function(doc) { @@ -48,13 +48,19 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten cur_frm.toggle_display("sales_order_no", false); cur_frm.fields_dict.indent_details.grid.set_column_disp("sales_order_no", false); } + }, + + validate_company_and_party: function(party_field) { + return true; + }, + + calculate_taxes_and_totals: function() { + return; } }); -var new_cscript = new erpnext.buying.MaterialRequestController({frm: cur_frm}); - // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new_cscript); +$.extend(cur_frm.cscript, new erpnext.buying.MaterialRequestController({frm: cur_frm})); cur_frm.cscript.qty = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; diff --git a/stock/doctype/purchase_receipt/purchase_receipt.txt b/stock/doctype/purchase_receipt/purchase_receipt.txt index 350777d95c..4e055dcd54 100755 --- a/stock/doctype/purchase_receipt/purchase_receipt.txt +++ b/stock/doctype/purchase_receipt/purchase_receipt.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-25 16:01:29", + "creation": "2013-05-21 16:16:39", "docstatus": 0, - "modified": "2013-02-02 19:09:37", + "modified": "2013-05-28 12:21:17", "modified_by": "Administrator", "owner": "Administrator" }, @@ -75,43 +75,48 @@ "width": "150px" }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "supplier_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Supplier Name", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -569,6 +574,7 @@ "oldfieldtype": "Text Editor" }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -888,7 +894,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material Manager", @@ -911,7 +916,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material User", @@ -934,7 +938,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "permlevel": 0, "report": 1, "role": "Purchase User", @@ -946,7 +949,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "Purchase User", "submit": 0 diff --git a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt index 8cef6a3534..9f7d72c071 100755 --- a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt +++ b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-07 11:42:59", + "creation": "2013-05-24 19:29:10", "docstatus": 0, - "modified": "2013-05-22 12:01:08", + "modified": "2013-05-28 12:13:59", "modified_by": "Administrator", "owner": "Administrator" }, @@ -357,7 +357,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "width": "100px" }, { @@ -370,7 +370,7 @@ "oldfieldname": "prevdoc_doctype", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 0 + "read_only": 1 }, { "doctype": "DocField", diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index f1a06c9bdb..5b716fcc53 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -192,7 +192,8 @@ class TransactionBase(DocListController): # Get Supplier Default Primary Address - first load # ----------------------- def get_default_supplier_address(self, args): - args = load_json(args) + if isinstance(args, basestring): + args = load_json(args) address_text, address_name = self.get_address_text(supplier=args['supplier']) ret = { 'supplier_address' : address_name, From ca63548ae08b3eaa08e411167ab8945547342bb5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 28 May 2013 17:42:41 +0530 Subject: [PATCH 17/17] [test sales invoice] [fix] fixed values of various test cases --- .../doctype/sales_invoice/test_sales_invoice.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index 7c4b8b1ea4..f0ff124898 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -327,7 +327,7 @@ class TestSalesInvoice(unittest.TestCase): si.doc.name, as_dict=1)[0] self.assertTrue(sle) self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty], - ["_Test Item", "_Test Warehouse", -5.0]) + ["_Test Item", "_Test Warehouse", -1.0]) # check gl entries stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company", @@ -343,8 +343,8 @@ class TestSalesInvoice(unittest.TestCase): [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, 375.0], - [pos[1]["expense_account"], 375.0, 0.0], + [stock_in_hand_account, 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] ]) @@ -658,7 +658,7 @@ test_records = [ "description": "VAT", "doctype": "Sales Taxes and Charges", "parentfield": "other_charges", - "tax_amount": 30.0, + "rate": 6, }, { "account_head": "_Test Account Service Tax - _TC", @@ -666,7 +666,7 @@ test_records = [ "description": "Service Tax", "doctype": "Sales Taxes and Charges", "parentfield": "other_charges", - "tax_amount": 31.8, + "rate": 6.36, }, { "parentfield": "sales_team", @@ -708,7 +708,7 @@ test_records = [ "description": "_Test Item", "doctype": "Sales Invoice Item", "parentfield": "entries", - "qty": 5.0, + "qty": 1.0, "basic_rate": 500.0, "amount": 500.0, "export_rate": 500.0, @@ -723,7 +723,7 @@ test_records = [ "description": "VAT", "doctype": "Sales Taxes and Charges", "parentfield": "other_charges", - "tax_amount": 80.0, + "rate": 16, }, { "account_head": "_Test Account Service Tax - _TC", @@ -731,7 +731,7 @@ test_records = [ "description": "Service Tax", "doctype": "Sales Taxes and Charges", "parentfield": "other_charges", - "tax_amount": 50.0, + "rate": 10 } ], [