// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt // Preset // ------ // cur_frm.cscript.tname - Details table name // cur_frm.cscript.fname - Details fieldname // cur_frm.cscript.other_fname - fieldname // cur_frm.cscript.sales_team_fname - Sales Team fieldname wn.provide("erpnext.selling"); wn.require("assets/erpnext/js/transaction.js"); {% include "public/js/controllers/accounts.js" %} erpnext.selling.SellingController = erpnext.TransactionController.extend({ onload: function() { this._super(); this.toggle_rounded_total(); this.setup_queries(); this.toggle_editable_price_list_rate(); }, setup_queries: function() { var me = this; this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate"); $.each([["customer_address", "customer_filter"], ["shipping_address_name", "customer_filter"], ["contact_person", "customer_filter"], ["customer", "customer"], ["lead", "lead"]], function(i, opts) { if(me.frm.fields_dict[opts[0]]) me.frm.set_query(opts[0], erpnext.queries[opts[1]]); }); if(this.frm.fields_dict.taxes_and_charges) { this.frm.set_query("taxes_and_charges", function() { return { filters: [ ['Sales Taxes and Charges Master', 'company', '=', me.frm.doc.company], ['Sales Taxes and Charges Master', 'docstatus', '!=', 2] ] } }); } if(this.frm.fields_dict.selling_price_list) { this.frm.set_query("selling_price_list", function() { return { filters: { selling: 1 } }; }); } if(!this.fname) { return; } if(this.frm.fields_dict[this.fname].grid.get_field('item_code')) { this.frm.set_query("item_code", this.fname, function() { return { query: "erpnext.controllers.queries.item_query", filters: (me.frm.doc.order_type === "Maintenance" ? {'is_service_item': 'Yes'}: {'is_sales_item': 'Yes' }) } }); } if(this.frm.fields_dict[this.fname].grid.get_field('batch_no')) { this.frm.set_query("batch_no", this.fname, function(doc, cdt, cdn) { var item = wn.model.get_doc(cdt, cdn); if(!item.item_code) { wn.throw(wn._("Please enter Item Code to get batch no")); } else { filters = { 'item_code': item.item_code, 'posting_date': me.frm.doc.posting_date, } if(item.warehouse) filters["warehouse"] = item.warehouse return { query : "erpnext.controllers.queries.get_batch_no", filters: filters } } }); } if(this.frm.fields_dict.sales_team && this.frm.fields_dict.sales_team.grid.get_field("sales_person")) { this.frm.set_query("sales_person", "sales_team", erpnext.queries.not_a_group_filter); } }, refresh: function() { this._super(); this.frm.toggle_display("customer_name", (this.frm.doc.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer)); if(this.frm.fields_dict.packing_details) { var packing_list_exists = this.frm.get_doclist({parentfield: "packing_details"}).length; this.frm.toggle_display("packing_list", packing_list_exists ? true : false); } }, customer: function() { erpnext.utils.get_party_details(this.frm); }, customer_address: function() { erpnext.utils.get_address_display(this.frm, "customer_address"); }, contact_person: function() { erpnext.utils.get_contact_details(this.frm); }, barcode: function(doc, cdt, cdn) { this.item_code(doc, cdt, cdn); }, item_code: function(doc, cdt, cdn) { var me = this; var item = wn.model.get_doc(cdt, cdn); if(item.item_code || item.barcode || item.serial_no) { if(!this.validate_company_and_party("customer")) { cur_frm.fields_dict[me.frm.cscript.fname].grid.grid_rows[item.idx - 1].remove(); } else { return this.frm.call({ method: "erpnext.stock.get_item_details.get_item_details", child: item, args: { args: { item_code: item.item_code, barcode: item.barcode, serial_no: item.serial_no, warehouse: item.warehouse, doctype: me.frm.doc.doctype, parentfield: item.parentfield, customer: me.frm.doc.customer, currency: me.frm.doc.currency, conversion_rate: me.frm.doc.conversion_rate, price_list: me.frm.doc.selling_price_list, price_list_currency: me.frm.doc.price_list_currency, plc_conversion_rate: me.frm.doc.plc_conversion_rate, company: me.frm.doc.company, order_type: me.frm.doc.order_type, is_pos: cint(me.frm.doc.is_pos), "transaction_type": "selling" } }, callback: function(r) { if(!r.exc) { me.frm.script_manager.trigger("ref_rate", cdt, cdn); } } }); } } }, selling_price_list: function() { this.get_price_list_currency("Selling"); }, ref_rate: function(doc, cdt, cdn) { var item = wn.model.get_doc(cdt, cdn); wn.model.round_floats_in(item, ["ref_rate", "adj_rate"]); item.export_rate = flt(item.ref_rate * (1 - item.adj_rate / 100.0), precision("export_rate", item)); this.calculate_taxes_and_totals(); }, adj_rate: function(doc, cdt, cdn) { var item = wn.model.get_doc(cdt, cdn); if(!item.ref_rate) { item.adj_rate = 0.0; } else { this.ref_rate(doc, cdt, cdn); } }, export_rate: function(doc, cdt, cdn) { var item = wn.model.get_doc(cdt, cdn); wn.model.round_floats_in(item, ["export_rate", "ref_rate"]); if(item.ref_rate) { item.adj_rate = flt((1 - item.export_rate / item.ref_rate) * 100.0, precision("adj_rate", item)); } else { item.adj_rate = 0.0; } this.calculate_taxes_and_totals(); }, discount_amount: function() { this.calculate_taxes_and_totals(); }, commission_rate: function() { this.calculate_commission(); refresh_field("total_commission"); }, total_commission: function() { if(this.frm.doc.net_total) { wn.model.round_floats_in(this.frm.doc, ["net_total", "total_commission"]); if(this.frm.doc.net_total < this.frm.doc.total_commission) { var msg = (wn._("[Error]") + " " + wn._(wn.meta.get_label(this.frm.doc.doctype, "total_commission", this.frm.doc.name)) + " > " + wn._(wn.meta.get_label(this.frm.doc.doctype, "net_total", this.frm.doc.name))); msgprint(msg); throw msg; } this.frm.set_value("commission_rate", flt(this.frm.doc.total_commission * 100.0 / this.frm.doc.net_total)); } }, allocated_percentage: function(doc, cdt, cdn) { var sales_person = wn.model.get_doc(cdt, cdn); if(sales_person.allocated_percentage) { sales_person.allocated_percentage = flt(sales_person.allocated_percentage, precision("allocated_percentage", sales_person)); sales_person.allocated_amount = flt(this.frm.doc.net_total * sales_person.allocated_percentage / 100.0, precision("allocated_amount", sales_person)); refresh_field(["allocated_percentage", "allocated_amount"], sales_person.name, sales_person.parentfield); } }, warehouse: function(doc, cdt, cdn) { var item = wn.model.get_doc(cdt, cdn); if(item.item_code && item.warehouse) { return this.frm.call({ method: "erpnext.selling.utils.get_available_qty", child: item, args: { item_code: item.item_code, warehouse: item.warehouse, }, }); } }, toggle_rounded_total: function() { var me = this; if(cint(wn.defaults.get_global_default("disable_rounded_total"))) { $.each(["rounded_total", "rounded_total_export"], function(i, fieldname) { me.frm.set_df_property(fieldname, "print_hide", 1); me.frm.toggle_display(fieldname, false); }); } }, toggle_editable_price_list_rate: function() { var df = wn.meta.get_docfield(this.tname, "ref_rate", this.frm.doc.name); var editable_price_list_rate = cint(wn.defaults.get_default("editable_price_list_rate")); if(df && editable_price_list_rate) { df.read_only = 0; } }, calculate_taxes_and_totals: function() { this._super(); this.calculate_total_advance("Sales Invoice", "advance_adjustment_details"); this.calculate_commission(); this.calculate_contribution(); // TODO check for custom_recalc in custom scripts of server this.frm.refresh_fields(); }, calculate_item_values: function() { var me = this; if (!this.discount_amount_applied) { $.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)); 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"); }); } }, determine_exclusive_rate: function() { var me = this; $.each(me.frm.item_doclist, function(n, item) { var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); var cumulated_tax_fraction = 0.0; $.each(me.frm.tax_doclist, function(i, tax) { tax.tax_fraction_for_current_item = me.get_current_tax_fraction(tax, item_tax_map); if(i==0) { tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item; } else { tax.grand_total_fraction_for_current_item = me.frm.tax_doclist[i-1].grand_total_fraction_for_current_item + tax.tax_fraction_for_current_item; } cumulated_tax_fraction += tax.tax_fraction_for_current_item; }); if(cumulated_tax_fraction && !me.discount_amount_applied) { item.amount = flt( (item.export_amount * me.frm.doc.conversion_rate) / (1 + cumulated_tax_fraction), precision("amount", item)); item.basic_rate = flt(item.amount / item.qty, precision("basic_rate", item)); if(item.adj_rate == 100) { item.base_ref_rate = item.basic_rate; item.basic_rate = 0.0; } else { item.base_ref_rate = flt(item.basic_rate / (1 - item.adj_rate / 100.0), precision("base_ref_rate", item)); } } }); }, get_current_tax_fraction: function(tax, item_tax_map) { // Get tax fraction for calculating tax exclusive amount // from tax inclusive amount var current_tax_fraction = 0.0; if(cint(tax.included_in_print_rate)) { var tax_rate = this._get_tax_rate(tax, item_tax_map); if(tax.charge_type == "On Net Total") { current_tax_fraction = (tax_rate / 100.0); } else if(tax.charge_type == "On Previous Row Amount") { current_tax_fraction = (tax_rate / 100.0) * this.frm.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item; } else if(tax.charge_type == "On Previous Row Total") { current_tax_fraction = (tax_rate / 100.0) * this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item; } } return current_tax_fraction; }, calculate_net_total: function() { var me = this; this.frm.doc.net_total = this.frm.doc.net_total_export = 0.0; $.each(this.frm.item_doclist, function(i, item) { me.frm.doc.net_total += item.amount; me.frm.doc.net_total_export += item.export_amount; }); wn.model.round_floats_in(this.frm.doc, ["net_total", "net_total_export"]); }, calculate_totals: function() { var me = this; var tax_count = this.frm.tax_doclist.length; this.frm.doc.grand_total = flt( tax_count ? this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total, precision("grand_total")); this.frm.doc.grand_total_export = flt(this.frm.doc.grand_total / this.frm.doc.conversion_rate, precision("grand_total_export")); this.frm.doc.other_charges_total = flt(this.frm.doc.grand_total - this.frm.doc.net_total, precision("other_charges_total")); this.frm.doc.other_charges_total_export = flt(this.frm.doc.grand_total_export - this.frm.doc.net_total_export + flt(this.frm.doc.discount_amount), precision("other_charges_total_export")); this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total); this.frm.doc.rounded_total_export = Math.round(this.frm.doc.grand_total_export); }, apply_discount_amount: function() { var me = this; var distributed_amount = 0.0; if (this.frm.doc.discount_amount) { var grand_total_for_discount_amount = this.get_grand_total_for_discount_amount(); // calculate item amount after Discount Amount if (grand_total_for_discount_amount) { $.each(this.frm.item_doclist, function(i, item) { distributed_amount = flt(me.frm.doc.discount_amount) * item.amount / grand_total_for_discount_amount; item.amount = flt(item.amount - distributed_amount, precision("amount", item)); }); this.discount_amount_applied = true; this._calculate_taxes_and_totals(); } } }, get_grand_total_for_discount_amount: function() { var me = this; var total_actual_tax = 0.0; var actual_taxes_dict = {}; $.each(this.frm.tax_doclist, function(i, tax) { if (tax.charge_type == "Actual") actual_taxes_dict[tax.idx] = tax.tax_amount; else if (actual_taxes_dict[tax.row_id] !== null) { actual_tax_amount = flt(actual_taxes_dict[tax.row_id]) * flt(tax.rate) / 100; actual_taxes_dict[tax.idx] = actual_tax_amount; } }); $.each(actual_taxes_dict, function(key, value) { if (value) total_actual_tax += value; }); grand_total_for_discount_amount = flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total")); return grand_total_for_discount_amount; }, calculate_outstanding_amount: function() { // NOTE: // paid_amount and write_off_amount is only for POS Invoice // total_advance is only for non POS Invoice if(this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.docstatus==0) { wn.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount", "paid_amount"]); var total_amount_to_pay = this.frm.doc.grand_total - this.frm.doc.write_off_amount - this.frm.doc.total_advance; this.frm.doc.paid_amount = this.frm.doc.is_pos? flt(total_amount_to_pay): 0.0; this.frm.doc.outstanding_amount = flt(total_amount_to_pay - this.frm.doc.paid_amount, precision("outstanding_amount")); } }, calculate_commission: function() { if(this.frm.fields_dict.commission_rate) { if(this.frm.doc.commission_rate > 100) { var msg = wn._(wn.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) + " " + wn._("cannot be greater than 100"); msgprint(msg); throw msg; } this.frm.doc.total_commission = flt(this.frm.doc.net_total * this.frm.doc.commission_rate / 100.0, precision("total_commission")); } }, calculate_contribution: function() { var me = this; $.each(wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, {parentfield: "sales_team"}), function(i, sales_person) { wn.model.round_floats_in(sales_person); if(sales_person.allocated_percentage) { sales_person.allocated_amount = flt( me.frm.doc.net_total * sales_person.allocated_percentage / 100.0, precision("allocated_amount", sales_person)); } }); }, _cleanup: function() { this._super(); this.frm.doc.in_words = this.frm.doc.in_words_export = ""; }, shipping_rule: function() { var me = this; if(this.frm.doc.shipping_rule) { return this.frm.call({ doc: this.frm.doc, method: "apply_shipping_rule", callback: function(r) { if(!r.exc) { me.calculate_taxes_and_totals(); } } }) } }, set_dynamic_labels: function() { this._super(); set_sales_bom_help(this.frm.doc); }, change_form_labels: function(company_currency) { var me = this; var field_label_map = {}; var setup_field_label_map = function(fields_list, currency) { $.each(fields_list, function(i, fname) { var docfield = wn.meta.docfield_map[me.frm.doc.doctype][fname]; if(docfield) { var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); field_label_map[fname] = label.trim() + " (" + currency + ")"; } }); }; setup_field_label_map(["net_total", "other_charges_total", "grand_total", "rounded_total", "in_words", "outstanding_amount", "total_advance", "paid_amount", "write_off_amount"], company_currency); setup_field_label_map(["net_total_export", "other_charges_total_export", "grand_total_export", "rounded_total_export", "in_words_export"], this.frm.doc.currency); cur_frm.set_df_property("conversion_rate", "description", "1 " + this.frm.doc.currency + " = [?] " + company_currency) if(this.frm.doc.price_list_currency && this.frm.doc.price_list_currency!=company_currency) { cur_frm.set_df_property("plc_conversion_rate", "description", "1 " + this.frm.doc.price_list_currency + " = [?] " + company_currency) } // toggle fields this.frm.toggle_display(["conversion_rate", "net_total", "other_charges_total", "grand_total", "rounded_total", "in_words"], this.frm.doc.currency != company_currency); this.frm.toggle_display(["plc_conversion_rate", "price_list_currency"], this.frm.doc.price_list_currency != company_currency); // set labels $.each(field_label_map, function(fname, label) { me.frm.fields_dict[fname].set_label(label); }); }, change_grid_labels: function(company_currency) { var me = this; var field_label_map = {}; var setup_field_label_map = function(fields_list, currency, parentfield) { var grid_doctype = me.frm.fields_dict[parentfield].grid.doctype; $.each(fields_list, function(i, fname) { var docfield = wn.meta.docfield_map[grid_doctype][fname]; if(docfield) { var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); field_label_map[grid_doctype + "-" + fname] = label.trim() + " (" + currency + ")"; } }); } setup_field_label_map(["basic_rate", "base_ref_rate", "amount"], company_currency, this.fname); setup_field_label_map(["export_rate", "ref_rate", "export_amount"], this.frm.doc.currency, this.fname); setup_field_label_map(["tax_amount", "total"], company_currency, "other_charges"); if(this.frm.fields_dict["advance_allocation_details"]) { setup_field_label_map(["advance_amount", "allocated_amount"], company_currency, "advance_allocation_details"); } // toggle columns var item_grid = this.frm.fields_dict[this.fname].grid; var show = (this.frm.doc.currency != company_currency) || (wn.model.get_doclist(cur_frm.doctype, cur_frm.docname, {parentfield: "other_charges", included_in_print_rate: 1}).length); $.each(["basic_rate", "base_ref_rate", "amount"], function(i, fname) { if(wn.meta.get_docfield(item_grid.doctype, fname)) item_grid.set_column_disp(fname, show); }); // set labels var $wrapper = $(this.frm.wrapper); $.each(field_label_map, function(fname, label) { fname = fname.split("-"); var df = wn.meta.get_docfield(fname[0], fname[1], me.frm.doc.name); if(df) df.label = label; }); }, shipping_address_name: function () { var me = this; if(this.frm.doc.shipping_address_name) { wn.model.with_doc("Address", this.frm.doc.shipping_address_name, function(name) { var address = wn.model.get_doc("Address", name); var out = $.map(["address_line1", "address_line2", "city"], function(f) { return address[f]; }); var state_pincode = $.map(["state", "pincode"], function(f) { return address[f]; }).join(" "); if(state_pincode) out.push(state_pincode); if(address["country"]) out.push(address["country"]); out.concat($.map([["Phone:", address["phone"]], ["Fax:", address["fax"]]], function(val) { return val[1] ? val.join(" ") : null; })); me.frm.set_value("shipping_address", out.join("\n")); }); } } }); // Help for Sales BOM items var set_sales_bom_help = function(doc) { if(!cur_frm.fields_dict.packing_list) return; if (getchildren('Packed Item', doc.name, 'packing_details').length) { $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true); if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { help_msg = "<div class='alert alert-warning'>" + wn._("For 'Sales BOM' items, warehouse, serial no and batch no \ will be considered from the 'Packing List' table. \ If warehouse and batch no are same for all packing items for any 'Sales BOM' item, \ those values can be entered in the main item table, values will be copied to 'Packing List' table.")+ "</div>"; wn.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = help_msg; } } else { $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false); if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { wn.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = ''; } } refresh_field('sales_bom_help'); }