| // 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 |
| |
| frappe.provide("erpnext.selling"); |
| frappe.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 = frappe.model.get_doc(cdt, cdn); |
| if(!item.item_code) { |
| frappe.throw(frappe._("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"); |
| }, |
| |
| shipping_address_name: function() { |
| erpnext.utils.get_address_display(this.frm, "shipping_address_name", "shipping_address"); |
| }, |
| |
| contact_person: function() { |
| erpnext.utils.get_contact_details(this.frm); |
| }, |
| |
| barcode: function(doc, cdt, cdn) { |
| this.item_code(doc, cdt, cdn); |
| }, |
| |
| selling_price_list: function() { |
| this.get_price_list_currency("Selling"); |
| }, |
| |
| price_list_rate: function(doc, cdt, cdn) { |
| var item = frappe.model.get_doc(cdt, cdn); |
| frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]); |
| |
| item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0), |
| precision("rate", item)); |
| |
| this.calculate_taxes_and_totals(); |
| }, |
| |
| discount_percentage: function(doc, cdt, cdn) { |
| var item = frappe.model.get_doc(cdt, cdn); |
| if(!item.price_list_rate) { |
| item.discount_percentage = 0.0; |
| } else { |
| this.price_list_rate(doc, cdt, cdn); |
| } |
| }, |
| |
| rate: function(doc, cdt, cdn) { |
| var item = frappe.model.get_doc(cdt, cdn); |
| frappe.model.round_floats_in(item, ["rate", "price_list_rate"]); |
| |
| if(item.price_list_rate) { |
| item.discount_percentage = flt((1 - item.rate / item.price_list_rate) * 100.0, |
| precision("discount_percentage", item)); |
| } else { |
| item.discount_percentage = 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) { |
| frappe.model.round_floats_in(this.frm.doc, ["net_total", "total_commission"]); |
| |
| if(this.frm.doc.net_total < this.frm.doc.total_commission) { |
| var msg = (frappe._("[Error]") + " " + |
| frappe._(frappe.meta.get_label(this.frm.doc.doctype, "total_commission", |
| this.frm.doc.name)) + " > " + |
| frappe._(frappe.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 = frappe.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 = frappe.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(frappe.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 = frappe.meta.get_docfield(this.tname, "price_list_rate", this.frm.doc.name); |
| var editable_price_list_rate = cint(frappe.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) { |
| frappe.model.round_floats_in(item); |
| item.amount = flt(item.rate * item.qty, precision("amount", item)); |
| |
| me._set_in_company_currency(item, "price_list_rate", "base_price_list_rate"); |
| me._set_in_company_currency(item, "rate", "base_rate"); |
| me._set_in_company_currency(item, "amount", "base_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.base_amount = flt( |
| (item.amount * me.frm.doc.conversion_rate) / (1 + cumulated_tax_fraction), |
| precision("base_amount", item)); |
| |
| item.base_rate = flt(item.base_amount / item.qty, precision("base_rate", item)); |
| |
| if(item.discount_percentage == 100) { |
| item.base_price_list_rate = item.base_rate; |
| item.base_rate = 0.0; |
| } else { |
| item.base_price_list_rate = flt(item.base_rate / (1 - item.discount_percentage / 100.0), |
| precision("base_price_list_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.base_amount; |
| me.frm.doc.net_total_export += item.amount; |
| }); |
| |
| frappe.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.base_amount / grand_total_for_discount_amount; |
| item.base_amount = flt(item.base_amount - distributed_amount, precision("base_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) { |
| frappe.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 = frappe._(frappe.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) + |
| " " + frappe._("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(frappe.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, |
| {parentfield: "sales_team"}), function(i, sales_person) { |
| frappe.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 = frappe.meta.docfield_map[me.frm.doc.doctype][fname]; |
| if(docfield) { |
| var label = frappe._(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 = frappe.meta.docfield_map[grid_doctype][fname]; |
| if(docfield) { |
| var label = frappe._(docfield.label || "").replace(/\([^\)]*\)/g, ""); |
| field_label_map[grid_doctype + "-" + fname] = |
| label.trim() + " (" + currency + ")"; |
| } |
| }); |
| } |
| |
| setup_field_label_map(["base_rate", "base_price_list_rate", "base_amount"], |
| company_currency, this.fname); |
| |
| setup_field_label_map(["rate", "price_list_rate", "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) || |
| (frappe.model.get_doclist(cur_frm.doctype, cur_frm.docname, |
| {parentfield: "other_charges", included_in_print_rate: 1}).length); |
| |
| $.each(["base_rate", "base_price_list_rate", "base_amount"], function(i, fname) { |
| if(frappe.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 = frappe.meta.get_docfield(fname[0], fname[1], me.frm.doc.name); |
| if(df) df.label = label; |
| }); |
| }, |
| }); |
| |
| // 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'>" + |
| frappe._("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>"; |
| frappe.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)) { |
| frappe.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = ''; |
| } |
| } |
| refresh_field('sales_bom_help'); |
| } |