From 3543f3004692164e7405937ffd7d94165ca8fa3f Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 24 May 2013 19:25:01 +0530 Subject: [PATCH] [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 b498ce25af2..3ddf907fdde 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 b63a176ebab..7f8ad63a9e5 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 a70c932d9b2..ebd44d4a60d 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 505848a3c84..7c4b8b1ea43 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 b1ab40be123..320212254f6 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 afe4741c862..ad2db915778 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 218d4bfeee0..bf59389213e 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 1e4f6cbfb7d..4e5b62b4e5a 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 952d70a602c..2c29e2c2756 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 19b2a50ff94..1499e0a3064 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 1fc411fad42..8313b5409da 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 01c8c617024..de90fa7a13f 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 00000000000..34e10f6f566 --- /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 078baf19974..a7c542196e7 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 fd3f5c58df1..4d97034210b 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 b7927543847..79f20bc02b5 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 0a31dfe80e4..df5e7614b4e 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 21bc141659b..3d4eed4842a 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 8a899b3a400..9f19a5b10bf 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 82e494cfb75..20740e76ab6 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});