From 3b4f481ca4723565614f098f5d4daeb7d1afd889 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 1 Apr 2019 17:56:49 +0530 Subject: [PATCH 01/25] fix: Make Customer and lead field dynamic --- .../selling/doctype/quotation/quotation.js | 43 +++-- .../selling/doctype/quotation/quotation.json | 151 ++++++++++++------ 2 files changed, 132 insertions(+), 62 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 6f7bfb3febf..7d5118cff21 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -8,7 +8,16 @@ frappe.ui.form.on('Quotation', { setup: function(frm) { frm.custom_make_buttons = { 'Sales Order': 'Make Sales Order' - } + }, + + frm.set_query("quotation_to", function() { + return{ + "filters": { + "name": ["in", ["Customer", "Lead"]], + } + } + }); + }, refresh: function(frm) { @@ -97,24 +106,30 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ }, - quotation_to: function() { - var me = this; - if (this.frm.doc.quotation_to == "Lead") { - this.frm.set_value("customer", null); - this.frm.set_value("contact_person", null); - } else if (this.frm.doc.quotation_to == "Customer") { - this.frm.set_value("lead", null); + quotation_to: function(frm) { + this.set_dynamic_field_label(); + this.toggle_reqd_lead_customer(); + }, + + set_dynamic_field_label: function(){ + if (this.frm.doc.quotation_to == "Customer") + { + this.frm.set_df_property("customer_lead", "label", "Customer"); } - this.toggle_reqd_lead_customer(); + if (this.frm.doc.quotation_to == "Lead") + { + this.frm.set_df_property("customer_lead", "label", "Lead"); + + this.frm.fields_dict.customer_lead.get_query = function() { + return{ query: "erpnext.controllers.queries.lead_query" } + } + } }, toggle_reqd_lead_customer: function() { var me = this; - this.frm.toggle_reqd("lead", this.frm.doc.quotation_to == "Lead"); - this.frm.toggle_reqd("customer", this.frm.doc.quotation_to == "Customer"); - // to overwrite the customer_filter trigger from queries.js this.frm.set_query('customer_address', erpnext.queries.address_query); this.frm.set_query('shipping_address_name', erpnext.queries.address_query); @@ -163,10 +178,6 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ cur_frm.script_manager.make(erpnext.selling.QuotationController); -cur_frm.fields_dict.lead.get_query = function(doc,cdt,cdn) { - return{ query: "erpnext.controllers.queries.lead_query" } -} - cur_frm.cscript['Make Sales Order'] = function() { frappe.model.open_mapped_doc({ method: "erpnext.selling.doctype.quotation.quotation.make_sales_order", diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 6cc09933e8e..6ff33bdb621 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -20,6 +20,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_section", "fieldtype": "Section Break", "hidden": 0, @@ -53,6 +54,7 @@ "collapsible": 0, "columns": 0, "default": "{customer_name}", + "fetch_if_empty": 0, "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -86,6 +88,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", "hidden": 0, @@ -121,8 +124,9 @@ "collapsible": 0, "columns": 0, "default": "Customer", + "fetch_if_empty": 0, "fieldname": "quotation_to", - "fieldtype": "Select", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -135,7 +139,7 @@ "no_copy": 0, "oldfieldname": "quotation_to", "oldfieldtype": "Select", - "options": "\nLead\nCustomer", + "options": "DocType", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -155,9 +159,10 @@ "bold": 1, "collapsible": 0, "columns": 0, - "depends_on": "eval:doc.quotation_to == \"Customer\"", - "fieldname": "customer", - "fieldtype": "Link", + "depends_on": "", + "fetch_if_empty": 0, + "fieldname": "customer_lead", + "fieldtype": "Dynamic Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -165,59 +170,24 @@ "in_global_search": 1, "in_list_view": 0, "in_standard_filter": 1, - "label": "Customer", + "label": "", "length": 0, "no_copy": 0, "oldfieldname": "customer", "oldfieldtype": "Link", - "options": "Customer", + "options": "quotation_to", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.quotation_to == \"Lead\"", - "fieldname": "lead", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Lead", - "length": 0, - "no_copy": 0, - "oldfieldname": "lead", - "oldfieldtype": "Link", - "options": "Lead", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -226,6 +196,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "customer.customer_name", + "fetch_if_empty": 0, "fieldname": "customer_name", "fieldtype": "Data", "hidden": 1, @@ -258,6 +229,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break1", "fieldtype": "Column Break", "hidden": 0, @@ -290,6 +262,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -326,6 +299,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -362,6 +336,7 @@ "collapsible": 0, "columns": 0, "default": "Today", + "fetch_if_empty": 0, "fieldname": "transaction_date", "fieldtype": "Date", "hidden": 0, @@ -396,6 +371,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "valid_till", "fieldtype": "Date", "hidden": 0, @@ -429,6 +405,7 @@ "collapsible": 0, "columns": 0, "default": "Sales", + "fetch_if_empty": 0, "fieldname": "order_type", "fieldtype": "Select", "hidden": 0, @@ -465,6 +442,7 @@ "collapsible_depends_on": "", "columns": 0, "depends_on": "eval:(doc.customer || doc.lead)", + "fetch_if_empty": 0, "fieldname": "contact_section", "fieldtype": "Section Break", "hidden": 0, @@ -497,6 +475,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_address", "fieldtype": "Link", "hidden": 0, @@ -529,6 +508,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "address_display", "fieldtype": "Small Text", "hidden": 0, @@ -563,6 +543,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.customer", + "fetch_if_empty": 0, "fieldname": "contact_person", "fieldtype": "Link", "hidden": 0, @@ -597,6 +578,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_display", "fieldtype": "Small Text", "hidden": 0, @@ -628,6 +610,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_mobile", "fieldtype": "Small Text", "hidden": 0, @@ -659,6 +642,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_email", "fieldtype": "Data", "hidden": 1, @@ -692,6 +676,7 @@ "collapsible": 0, "columns": 0, "depends_on": "customer", + "fetch_if_empty": 0, "fieldname": "col_break98", "fieldtype": "Column Break", "hidden": 0, @@ -723,6 +708,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "shipping_address_name", "fieldtype": "Link", "hidden": 0, @@ -755,6 +741,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "shipping_address", "fieldtype": "Small Text", "hidden": 0, @@ -788,6 +775,7 @@ "columns": 0, "depends_on": "customer", "description": "", + "fetch_if_empty": 0, "fieldname": "customer_group", "fieldtype": "Link", "hidden": 1, @@ -823,6 +811,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "territory", "fieldtype": "Link", "hidden": 0, @@ -855,6 +844,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "hidden": 0, @@ -887,6 +877,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "currency", "fieldtype": "Link", "hidden": 0, @@ -923,6 +914,7 @@ "collapsible": 0, "columns": 0, "description": "Rate at which customer's currency is converted to company's base currency", + "fetch_if_empty": 0, "fieldname": "conversion_rate", "fieldtype": "Float", "hidden": 0, @@ -958,6 +950,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break2", "fieldtype": "Column Break", "hidden": 0, @@ -989,6 +982,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "selling_price_list", "fieldtype": "Link", "hidden": 0, @@ -1024,6 +1018,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "price_list_currency", "fieldtype": "Link", "hidden": 0, @@ -1057,6 +1052,7 @@ "collapsible": 0, "columns": 0, "description": "Rate at which Price list currency is converted to company's base currency", + "fetch_if_empty": 0, "fieldname": "plc_conversion_rate", "fieldtype": "Float", "hidden": 0, @@ -1089,6 +1085,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "ignore_pricing_rule", "fieldtype": "Check", "hidden": 0, @@ -1120,6 +1117,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "items_section", "fieldtype": "Section Break", "hidden": 0, @@ -1153,6 +1151,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "items", "fieldtype": "Table", "hidden": 0, @@ -1188,6 +1187,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sec_break23", "fieldtype": "Section Break", "hidden": 0, @@ -1218,6 +1218,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_qty", "fieldtype": "Float", "hidden": 0, @@ -1250,6 +1251,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_total", "fieldtype": "Currency", "hidden": 0, @@ -1283,6 +1285,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_net_total", "fieldtype": "Currency", "hidden": 0, @@ -1318,6 +1321,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_28", "fieldtype": "Column Break", "hidden": 0, @@ -1348,6 +1352,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total", "fieldtype": "Currency", "hidden": 0, @@ -1381,6 +1386,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "net_total", "fieldtype": "Currency", "hidden": 0, @@ -1413,6 +1419,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_net_weight", "fieldtype": "Float", "hidden": 0, @@ -1445,6 +1452,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes_section", "fieldtype": "Section Break", "hidden": 0, @@ -1478,6 +1486,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes_and_charges", "fieldtype": "Link", "hidden": 0, @@ -1512,6 +1521,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_34", "fieldtype": "Column Break", "hidden": 0, @@ -1542,6 +1552,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "shipping_rule", "fieldtype": "Link", "hidden": 0, @@ -1575,6 +1586,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_36", "fieldtype": "Section Break", "hidden": 0, @@ -1605,6 +1617,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes", "fieldtype": "Table", "hidden": 0, @@ -1639,6 +1652,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", "hidden": 0, @@ -1671,6 +1685,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "other_charges_calculation", "fieldtype": "Text", "hidden": 0, @@ -1703,6 +1718,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_39", "fieldtype": "Section Break", "hidden": 0, @@ -1733,6 +1749,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_total_taxes_and_charges", "fieldtype": "Currency", "hidden": 0, @@ -1767,6 +1784,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_42", "fieldtype": "Column Break", "hidden": 0, @@ -1797,6 +1815,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "hidden": 0, @@ -1830,6 +1849,7 @@ "collapsible": 1, "collapsible_depends_on": "discount_amount", "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_44", "fieldtype": "Section Break", "hidden": 0, @@ -1863,6 +1883,7 @@ "collapsible": 0, "columns": 0, "default": "Grand Total", + "fetch_if_empty": 0, "fieldname": "apply_discount_on", "fieldtype": "Select", "hidden": 0, @@ -1896,6 +1917,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_discount_amount", "fieldtype": "Currency", "hidden": 0, @@ -1929,6 +1951,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_46", "fieldtype": "Column Break", "hidden": 0, @@ -1960,6 +1983,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "additional_discount_percentage", "fieldtype": "Float", "hidden": 0, @@ -1992,6 +2016,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "discount_amount", "fieldtype": "Currency", "hidden": 0, @@ -2024,6 +2049,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "totals", "fieldtype": "Section Break", "hidden": 0, @@ -2057,6 +2083,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_grand_total", "fieldtype": "Currency", "hidden": 0, @@ -2092,6 +2119,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", "hidden": 0, @@ -2126,6 +2154,7 @@ "collapsible": 0, "columns": 0, "description": "In Words will be visible once you save the Quotation.", + "fetch_if_empty": 0, "fieldname": "base_in_words", "fieldtype": "Data", "hidden": 0, @@ -2160,6 +2189,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_rounded_total", "fieldtype": "Currency", "hidden": 0, @@ -2195,6 +2225,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break3", "fieldtype": "Column Break", "hidden": 0, @@ -2227,6 +2258,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "grand_total", "fieldtype": "Currency", "hidden": 0, @@ -2262,6 +2294,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "rounding_adjustment", "fieldtype": "Currency", "hidden": 0, @@ -2295,6 +2328,7 @@ "bold": 1, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "rounded_total", "fieldtype": "Currency", "hidden": 0, @@ -2330,6 +2364,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "in_words", "fieldtype": "Data", "hidden": 0, @@ -2366,6 +2401,7 @@ "collapsible_depends_on": "", "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "hidden": 0, @@ -2398,6 +2434,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_terms_template", "fieldtype": "Link", "hidden": 0, @@ -2431,6 +2468,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_schedule", "fieldtype": "Table", "hidden": 0, @@ -2465,6 +2503,7 @@ "collapsible": 1, "collapsible_depends_on": "terms", "columns": 0, + "fetch_if_empty": 0, "fieldname": "terms_section_break", "fieldtype": "Section Break", "hidden": 0, @@ -2498,6 +2537,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "tc_name", "fieldtype": "Link", "hidden": 0, @@ -2532,6 +2572,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "terms", "fieldtype": "Text Editor", "hidden": 0, @@ -2565,6 +2606,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "print_settings", "fieldtype": "Section Break", "hidden": 0, @@ -2597,6 +2639,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "letter_head", "fieldtype": "Link", "hidden": 0, @@ -2631,6 +2674,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "group_same_items", "fieldtype": "Check", "hidden": 0, @@ -2663,6 +2707,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_73", "fieldtype": "Column Break", "hidden": 0, @@ -2694,6 +2739,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "select_print_heading", "fieldtype": "Link", "hidden": 0, @@ -2728,6 +2774,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "language", "fieldtype": "Data", "hidden": 0, @@ -2760,6 +2807,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "subscription_section", "fieldtype": "Section Break", "hidden": 0, @@ -2792,6 +2840,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "auto_repeat", "fieldtype": "Link", "hidden": 0, @@ -2826,6 +2875,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval: doc.auto_repeat", + "fetch_if_empty": 0, "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", "hidden": 0, @@ -2858,6 +2908,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "more_info", "fieldtype": "Section Break", "hidden": 0, @@ -2891,6 +2942,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "campaign", "fieldtype": "Link", "hidden": 0, @@ -2925,6 +2977,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "source", "fieldtype": "Link", "hidden": 0, @@ -2960,6 +3013,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.status===\"Lost\"", + "fetch_if_empty": 0, "fieldname": "order_lost_reason", "fieldtype": "Small Text", "hidden": 0, @@ -2993,6 +3047,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break4", "fieldtype": "Column Break", "hidden": 0, @@ -3026,6 +3081,7 @@ "collapsible": 0, "columns": 0, "default": "Draft", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -3060,6 +3116,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "enq_det", "fieldtype": "Text", "hidden": 1, @@ -3093,6 +3150,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "supplier_quotation", "fieldtype": "Link", "hidden": 0, @@ -3126,6 +3184,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "opportunity", "fieldtype": "Link", "hidden": 0, @@ -3165,7 +3224,7 @@ "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2019-01-07 16:51:55.604845", + "modified": "2019-04-01 17:21:09.462834", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", @@ -3331,11 +3390,11 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 1, - "search_fields": "status,transaction_date,customer,lead,order_type", + "search_fields": "status,transaction_date,customer_lead,order_type", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "timeline_field": "customer", + "timeline_field": "customer_lead", "title_field": "title", "track_changes": 0, "track_seen": 0, From 6ba48c58e8ad6b6859fbed82ff7a6f1f57584796 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 1 Apr 2019 19:08:59 +0530 Subject: [PATCH 02/25] fix: Server side handling of quotation to and get_query fix --- .../selling/doctype/quotation/quotation.js | 8 +++---- .../selling/doctype/quotation/quotation.py | 22 +++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 7d5118cff21..dcd20917796 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -26,6 +26,8 @@ frappe.ui.form.on('Quotation', { quotation_to: function(frm) { frm.trigger("set_label"); + frm.trigger("set_dynamic_field_label"); + frm.trigger("toggle_reqd_lead_customer"); }, set_label: function(frm) { @@ -106,15 +108,11 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ }, - quotation_to: function(frm) { - this.set_dynamic_field_label(); - this.toggle_reqd_lead_customer(); - }, - set_dynamic_field_label: function(){ if (this.frm.doc.quotation_to == "Customer") { this.frm.set_df_property("customer_lead", "label", "Customer"); + this.frm.fields_dict.customer_lead.get_query = null; } if (this.frm.doc.quotation_to == "Lead") diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 729256068be..cea2b356c0d 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -28,11 +28,11 @@ class Quotation(SellingController): self.update_opportunity() self.validate_order_type() self.validate_uom_is_integer("stock_uom", "qty") - self.validate_quotation_to() + # self.validate_quotation_to() self.validate_valid_till() if self.items: self.with_items = 1 - + def validate_valid_till(self): if self.valid_till and self.valid_till < self.transaction_date: frappe.throw(_("Valid till date cannot be before transaction date")) @@ -43,16 +43,16 @@ class Quotation(SellingController): def validate_order_type(self): super(Quotation, self).validate_order_type() - def validate_quotation_to(self): - if self.customer: - self.quotation_to = "Customer" - self.lead = None - elif self.lead: - self.quotation_to = "Lead" + # def validate_quotation_to(self): + # if self.customer: + # self.quotation_to = "Customer" + # self.lead = None + # elif self.lead: + # self.quotation_to = "Lead" def update_lead(self): - if self.lead: - frappe.get_doc("Lead", self.lead).set_status(update=True) + if self.quotation_to == "Lead": + frappe.get_doc("Lead", self.customer_lead).set_status(update=True) def update_opportunity(self): for opportunity in list(set([d.prevdoc_docname for d in self.get("items")])): @@ -209,7 +209,7 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): } }, target_doc, set_missing_values, ignore_permissions=ignore_permissions) - return doclist + return doclist def _make_customer(source_name, ignore_permissions=False): quotation = frappe.db.get_value("Quotation", source_name, ["lead", "order_type", "customer"]) From a65af7a2f5c3e9122a00225cf090d9969ac1b746 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 1 Apr 2019 22:18:10 +0530 Subject: [PATCH 03/25] fix: Quotation Query fix in lead.py --- erpnext/crm/doctype/lead/lead.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 29ca71bd88e..d45c105ab8e 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -93,7 +93,7 @@ class Lead(SellingController): def has_quotation(self): return frappe.db.get_value("Quotation", { - "lead": self.name, + "customer_lead": self.name, "docstatus": 1, "status": ["!=", "Lost"] From ff994924812f5e909655aa975bc0acb3e0ed7501 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 1 Apr 2019 23:07:36 +0530 Subject: [PATCH 04/25] fix: Removed redundant code --- erpnext/selling/doctype/quotation/quotation.js | 12 ++++++------ erpnext/selling/doctype/quotation/quotation.py | 8 -------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index dcd20917796..6be55756c45 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -39,16 +39,16 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ onload: function(doc, dt, dn) { var me = this; this._super(doc, dt, dn); - if(doc.customer && !doc.quotation_to) - doc.quotation_to = "Customer"; - else if(doc.lead && !doc.quotation_to) - doc.quotation_to = "Lead"; + // if(doc.customer && !doc.quotation_to) + // doc.quotation_to = "Customer"; + // else if(doc.lead && !doc.quotation_to) + // doc.quotation_to = "Lead"; }, refresh: function(doc, dt, dn) { this._super(doc, dt, dn); - doctype = doc.quotation_to == 'Customer' ? 'Customer':'Lead'; - frappe.dynamic_link = {doc: this.frm.doc, fieldname: doctype.toLowerCase(), doctype: doctype} + // doctype = doc.quotation_to == 'Customer' ? 'Customer':'Lead'; + // frappe.dynamic_link = {doc: this.frm.doc, fieldname: doctype.toLowerCase(), doctype: doctype} var me = this; diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index cea2b356c0d..2a04ca68126 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -28,7 +28,6 @@ class Quotation(SellingController): self.update_opportunity() self.validate_order_type() self.validate_uom_is_integer("stock_uom", "qty") - # self.validate_quotation_to() self.validate_valid_till() if self.items: self.with_items = 1 @@ -43,13 +42,6 @@ class Quotation(SellingController): def validate_order_type(self): super(Quotation, self).validate_order_type() - # def validate_quotation_to(self): - # if self.customer: - # self.quotation_to = "Customer" - # self.lead = None - # elif self.lead: - # self.quotation_to = "Lead" - def update_lead(self): if self.quotation_to == "Lead": frappe.get_doc("Lead", self.customer_lead).set_status(update=True) From 163899443687476a014d2288bcf413374f464712 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 2 Apr 2019 10:15:59 +0530 Subject: [PATCH 05/25] fix: Patch to move customer and lead --- erpnext/patches.txt | 3 ++- .../v11_1/move_customer_lead_to_dynamic_column.py | 10 ++++++++++ erpnext/selling/doctype/quotation/quotation.js | 7 +------ erpnext/selling/doctype/quotation/quotation.json | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 9dcb285a0a3..e2929ca2ed9 100755 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -589,4 +589,5 @@ erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 erpnext.patches.v11_0.make_italian_localization_fields # 26-03-2019 erpnext.patches.v11_1.make_job_card_time_logs -erpnext.patches.v11_1.set_variant_based_on \ No newline at end of file +erpnext.patches.v11_1.set_variant_based_on +erpnext.patches.v11_1.move_customer_lead_to_dynamic_column diff --git a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py new file mode 100644 index 00000000000..305a4b01f9d --- /dev/null +++ b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + + frappe.db.dql(""" UPDATE `tabQuotation` set customer_lead = lead WHERE quotation_to = 'Lead' """) + frappe.db.dql(""" UPDATE `tabQuotation` set customer_lead = customer WHERE quotation_to = 'Customer' """) \ No newline at end of file diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 6be55756c45..195101ed6b4 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -39,16 +39,10 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ onload: function(doc, dt, dn) { var me = this; this._super(doc, dt, dn); - // if(doc.customer && !doc.quotation_to) - // doc.quotation_to = "Customer"; - // else if(doc.lead && !doc.quotation_to) - // doc.quotation_to = "Lead"; }, refresh: function(doc, dt, dn) { this._super(doc, dt, dn); - // doctype = doc.quotation_to == 'Customer' ? 'Customer':'Lead'; - // frappe.dynamic_link = {doc: this.frm.doc, fieldname: doctype.toLowerCase(), doctype: doctype} var me = this; @@ -129,6 +123,7 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ var me = this; // to overwrite the customer_filter trigger from queries.js + this.frm.toggle_reqd("customer_lead", this.frm.doc.quotation_to); this.frm.set_query('customer_address', erpnext.queries.address_query); this.frm.set_query('shipping_address_name', erpnext.queries.address_query); }, diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 6ff33bdb621..83de49e2ab5 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -182,7 +182,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 1, "set_only_once": 0, "translatable": 0, @@ -3224,7 +3224,7 @@ "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2019-04-01 17:21:09.462834", + "modified": "2019-04-02 10:06:27.850280", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", From e889a587243f8b4f70e4c77d698a012e44c569d7 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 2 Apr 2019 18:22:06 +0530 Subject: [PATCH 06/25] fix: Patch typo fix and set label in refresh --- erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py | 4 ++-- erpnext/selling/doctype/quotation/quotation.js | 2 +- erpnext/selling/doctype/quotation/quotation.json | 4 ++-- erpnext/selling/doctype/quotation/quotation.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py index 305a4b01f9d..23e91c7ebc7 100644 --- a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py +++ b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py @@ -6,5 +6,5 @@ import frappe def execute(): - frappe.db.dql(""" UPDATE `tabQuotation` set customer_lead = lead WHERE quotation_to = 'Lead' """) - frappe.db.dql(""" UPDATE `tabQuotation` set customer_lead = customer WHERE quotation_to = 'Customer' """) \ No newline at end of file + frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = lead WHERE quotation_to = 'Lead' """) + frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = customer WHERE quotation_to = 'Customer' """) \ No newline at end of file diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 195101ed6b4..cf8788963cc 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -22,11 +22,11 @@ frappe.ui.form.on('Quotation', { refresh: function(frm) { frm.trigger("set_label"); + frm.trigger("set_dynamic_field_label"); }, quotation_to: function(frm) { frm.trigger("set_label"); - frm.trigger("set_dynamic_field_label"); frm.trigger("toggle_reqd_lead_customer"); }, diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 83de49e2ab5..87a8aab3cfc 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -170,7 +170,7 @@ "in_global_search": 1, "in_list_view": 0, "in_standard_filter": 1, - "label": "", + "label": "Customer/Lead", "length": 0, "no_copy": 0, "oldfieldname": "customer", @@ -3224,7 +3224,7 @@ "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2019-04-02 10:06:27.850280", + "modified": "2019-04-02 10:18:58.506125", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 2a04ca68126..c1529bb322b 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -43,7 +43,7 @@ class Quotation(SellingController): super(Quotation, self).validate_order_type() def update_lead(self): - if self.quotation_to == "Lead": + if self.quotation_to == "Lead" and self.customer_lead: frappe.get_doc("Lead", self.customer_lead).set_status(update=True) def update_opportunity(self): From 3987b4b714b1749542726748bf6c6b4dcf999205 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Wed, 3 Apr 2019 08:53:03 +0530 Subject: [PATCH 07/25] fix: Test case fixes in quotation for dynamic column --- erpnext/crm/doctype/opportunity/opportunity.py | 4 ++-- erpnext/selling/doctype/customer/customer.py | 5 ++--- erpnext/selling/doctype/quotation/quotation.py | 2 +- erpnext/selling/doctype/quotation/test_quotation.py | 10 +++++----- erpnext/shopping_cart/test_shopping_cart.py | 9 ++++----- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index e8da4e6436b..102d7378287 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -218,8 +218,8 @@ def make_quotation(source_name, target_doc=None): quotation = frappe.get_doc(target) company_currency = frappe.get_cached_value('Company', quotation.company, "default_currency") - party_account_currency = get_party_account_currency("Customer", quotation.customer, - quotation.company) if quotation.customer else company_currency + party_account_currency = get_party_account_currency("Customer", quotation.customer_lead, + quotation.company) if quotation.customer_lead else company_currency quotation.currency = party_account_currency or company_currency diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 2970d7a5e82..139d76157e7 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -104,9 +104,8 @@ class Customer(TransactionBase): if self.lead_name: frappe.db.set_value('Lead', self.lead_name, 'status', 'Converted', update_modified=False) - for doctype in ('Opportunity', 'Quotation'): - for d in frappe.get_all(doctype, {'lead': self.lead_name}): - frappe.db.set_value(doctype, d.name, 'customer', self.name, update_modified=False) + for d in frappe.get_all('Opportunity', {'lead': self.lead_name}): + frappe.db.set_value('Opportunity', d.name, 'customer', self.name, update_modified=False) def create_lead_address_contact(self): if self.lead_name: diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index c1529bb322b..524a7a6039b 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -204,7 +204,7 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): return doclist def _make_customer(source_name, ignore_permissions=False): - quotation = frappe.db.get_value("Quotation", source_name, ["lead", "order_type", "customer"]) + quotation = frappe.db.get_value("Quotation", source_name, ["order_type", "customer_lead"]) if quotation and quotation[0] and not quotation[2]: lead_name = quotation[0] customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name}, diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 78fb0c14042..124c7922a80 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -203,15 +203,15 @@ class TestQuotation(unittest.TestCase): test_records = frappe.get_test_records('Quotation') -def get_quotation_dict(customer=None, item_code=None): - if not customer: - customer = '_Test Customer' +def get_quotation_dict(customer_lead=None, item_code=None): + if not customer_lead: + customer_lead = '_Test Customer' if not item_code: item_code = '_Test Item' return { 'doctype': 'Quotation', - 'customer': customer, + 'customer_lead': customer_lead, 'items': [ { 'item_code': item_code, @@ -229,7 +229,7 @@ def make_quotation(**args): qo.transaction_date = args.transaction_date qo.company = args.company or "_Test Company" - qo.customer = args.customer or "_Test Customer" + qo.customer_lead = args.customer_lead or "_Test Customer" qo.currency = args.currency or "INR" if args.selling_price_list: qo.selling_price_list = args.selling_price_list diff --git a/erpnext/shopping_cart/test_shopping_cart.py b/erpnext/shopping_cart/test_shopping_cart.py index 7d6b41ef5fc..d1124a662b6 100644 --- a/erpnext/shopping_cart/test_shopping_cart.py +++ b/erpnext/shopping_cart/test_shopping_cart.py @@ -33,7 +33,7 @@ class TestShoppingCart(unittest.TestCase): self.assertEqual(quotation.quotation_to, "Customer") self.assertEqual(quotation.contact_person, frappe.db.get_value("Contact", dict(email_id="test_cart_user@example.com"))) - self.assertEqual(quotation.lead, None) + self.assertEqual(quotation.customer_lead, None) self.assertEqual(quotation.contact_email, frappe.session.user) return quotation @@ -44,8 +44,7 @@ class TestShoppingCart(unittest.TestCase): # test if quotation with customer is fetched quotation = _get_cart_quotation() self.assertEqual(quotation.quotation_to, "Customer") - self.assertEqual(quotation.customer, "_Test Customer") - self.assertEqual(quotation.lead, None) + self.assertEqual(quotation.customer_lead, "_Test Customer") self.assertEqual(quotation.contact_email, frappe.session.user) return quotation @@ -107,7 +106,7 @@ class TestShoppingCart(unittest.TestCase): from erpnext.accounts.party import set_taxes - tax_rule_master = set_taxes(quotation.customer, "Customer", \ + tax_rule_master = set_taxes(quotation.customer_lead, "Customer", \ quotation.transaction_date, quotation.company, None, None, \ quotation.customer_address, quotation.shipping_address_name, 1) self.assertEqual(quotation.taxes_and_charges, tax_rule_master) @@ -122,7 +121,7 @@ class TestShoppingCart(unittest.TestCase): "doctype": "Quotation", "quotation_to": "Customer", "order_type": "Shopping Cart", - "customer": get_party(frappe.session.user).name, + "customer_lead": get_party(frappe.session.user).name, "docstatus": 0, "contact_email": frappe.session.user, "selling_price_list": "_Test Price List Rest of the World", From e2fc03e5613ea8c8b08b2d94efc4c817363fee6e Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Wed, 3 Apr 2019 10:50:09 +0530 Subject: [PATCH 08/25] fix: Cart test fixes --- erpnext/selling/doctype/quotation/quotation.py | 4 ++-- erpnext/shopping_cart/cart.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 524a7a6039b..dabb12bca42 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -205,8 +205,8 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): def _make_customer(source_name, ignore_permissions=False): quotation = frappe.db.get_value("Quotation", source_name, ["order_type", "customer_lead"]) - if quotation and quotation[0] and not quotation[2]: - lead_name = quotation[0] + if quotation and quotation[1]: + lead_name = quotation[1] customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name}, ["name", "customer_name"], as_dict=True) if not customer_name: diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index cc3205c7d70..691b960d05a 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -192,7 +192,7 @@ def _get_cart_quotation(party=None): party = get_party() quotation = frappe.get_all("Quotation", fields=["name"], filters= - {party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0}, + {"customer_lead": party.name, "order_type": "Shopping Cart", "docstatus": 0}, order_by="modified desc", limit_page_length=1) if quotation: From f094662f5e07a860fe0130462cfadb77d80256a0 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 8 Apr 2019 12:28:50 +0530 Subject: [PATCH 09/25] fix:Test record fixes --- .../move_customer_lead_to_dynamic_column.py | 2 +- .../doctype/quotation/test_records.json | 48 +++++++++---------- erpnext/shopping_cart/cart.py | 10 ++-- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py index 23e91c7ebc7..345b4ac714a 100644 --- a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py +++ b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py @@ -5,6 +5,6 @@ from __future__ import unicode_literals import frappe def execute(): - + frappe.reload_doctype("Quotation") frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = lead WHERE quotation_to = 'Lead' """) frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = customer WHERE quotation_to = 'Customer' """) \ No newline at end of file diff --git a/erpnext/selling/doctype/quotation/test_records.json b/erpnext/selling/doctype/quotation/test_records.json index 7a9d3eb1e25..03acea35cfa 100644 --- a/erpnext/selling/doctype/quotation/test_records.json +++ b/erpnext/selling/doctype/quotation/test_records.json @@ -1,36 +1,36 @@ [ { - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "customer": "_Test Customer", - "customer_group": "_Test Customer Group", - "customer_name": "_Test Customer", - "doctype": "Quotation", - "base_grand_total": 1000.0, - "grand_total": 1000.0, - "order_type": "Sales", - "plc_conversion_rate": 1.0, - "price_list_currency": "INR", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "customer_lead": "_Test Customer", + "customer_group": "_Test Customer Group", + "customer_name": "_Test Customer", + "doctype": "Quotation", + "base_grand_total": 1000.0, + "grand_total": 1000.0, + "order_type": "Sales", + "plc_conversion_rate": 1.0, + "price_list_currency": "INR", "items": [ { - "base_amount": 1000.0, - "base_rate": 100.0, - "description": "CPU", - "doctype": "Quotation Item", - "item_code": "_Test Item Home Desktop 100", - "item_name": "CPU", - "parentfield": "items", - "qty": 10.0, + "base_amount": 1000.0, + "base_rate": 100.0, + "description": "CPU", + "doctype": "Quotation Item", + "item_code": "_Test Item Home Desktop 100", + "item_name": "CPU", + "parentfield": "items", + "qty": 10.0, "rate": 100.0, "uom": "_Test UOM 1", "stock_uom": "_Test UOM 1", "conversion_factor": 1.0 } - ], - "quotation_to": "Customer", - "selling_price_list": "_Test Price List", - "territory": "_Test Territory", + ], + "quotation_to": "Customer", + "selling_price_list": "_Test Price List", + "territory": "_Test Territory", "transaction_date": "2013-02-21", "valid_till": "2013-03-21" } diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 691b960d05a..a3ad5a04ea5 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -207,7 +207,7 @@ def _get_cart_quotation(party=None): "status": "Draft", "docstatus": 0, "__islocal": 1, - (party.doctype.lower()): party.name + "customer_lead": party.name }) qdoc.contact_person = frappe.db.get_value("Contact", {"email_id": frappe.session.user}) @@ -287,9 +287,9 @@ def _set_price_list(quotation, cart_settings): # check if customer price list exists selling_price_list = None - if quotation.customer: + if quotation.customer_lead: from erpnext.accounts.party import get_default_price_list - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.customer)) + selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.customer_lead)) # else check for territory based price list if not selling_price_list: @@ -301,9 +301,9 @@ def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" from erpnext.accounts.party import set_taxes - customer_group = frappe.db.get_value("Customer", quotation.customer, "customer_group") + customer_group = frappe.db.get_value("Customer", quotation.customer_lead, "customer_group") - quotation.taxes_and_charges = set_taxes(quotation.customer, "Customer", \ + quotation.taxes_and_charges = set_taxes(quotation.customer_lead, "Customer", \ quotation.transaction_date, quotation.company, customer_group, None, \ quotation.customer_address, quotation.shipping_address_name, 1) # From 7f7a1b48ed1b2db6629169a9d82e0ea98fe56496 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 8 Apr 2019 21:53:05 +0530 Subject: [PATCH 10/25] fix: Test case fixes inquotation --- erpnext/selling/doctype/quotation/quotation.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index dabb12bca42..8feb3b9d5f4 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -122,8 +122,8 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): def set_missing_values(source, target): if customer: - target.customer = customer.name - target.customer_name = customer.customer_name + target.customer = customer + target.customer_name = customer target.ignore_pricing_rule = 1 target.flags.ignore_permissions = ignore_permissions target.run_method("set_missing_values") @@ -169,8 +169,8 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): def set_missing_values(source, target): if customer: - target.customer = customer.name - target.customer_name = customer.customer_name + target.customer = customer + target.customer_name = customer target.ignore_pricing_rule = 1 target.flags.ignore_permissions = ignore_permissions target.run_method("set_missing_values") @@ -204,8 +204,8 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): return doclist def _make_customer(source_name, ignore_permissions=False): - quotation = frappe.db.get_value("Quotation", source_name, ["order_type", "customer_lead"]) - if quotation and quotation[1]: + quotation = frappe.db.get_value("Quotation", source_name, ["order_type", "customer_lead", "customer_name"]) + if quotation and quotation[1] and not quotation[2]: lead_name = quotation[1] customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name}, ["name", "customer_name"], as_dict=True) @@ -234,3 +234,5 @@ def _make_customer(source_name, ignore_permissions=False): frappe.throw(_("Please create Customer from Lead {0}").format(lead_name)) else: return customer_name + else: + return quotation[2] From 4f0a4a1a2dc5452eb6c3c26afc856201607f0fa6 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Wed, 10 Apr 2019 16:04:22 +0530 Subject: [PATCH 11/25] fix: Customer dashboard fixes --- erpnext/selling/doctype/customer/customer_dashboard.py | 3 +++ erpnext/selling/doctype/quotation/quotation.py | 10 +++++----- erpnext/shopping_cart/test_shopping_cart.py | 1 - 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py index f2f430a61e2..b7175c9675d 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -6,6 +6,9 @@ def get_data(): 'heatmap': True, 'heatmap_message': _('This is based on transactions against this Customer. See timeline below for details'), 'fieldname': 'customer', + 'non_standard_fieldnames': { + 'Quotation': 'customer_lead' + }, 'transactions': [ { 'label': _('Pre Sales'), diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 8feb3b9d5f4..e99b635af9b 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -122,8 +122,8 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): def set_missing_values(source, target): if customer: - target.customer = customer - target.customer_name = customer + target.customer = customer.name + target.customer_name = customer.customer_name target.ignore_pricing_rule = 1 target.flags.ignore_permissions = ignore_permissions target.run_method("set_missing_values") @@ -169,8 +169,8 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): def set_missing_values(source, target): if customer: - target.customer = customer - target.customer_name = customer + target.customer = customer.name + target.customer_name = customer.customer_name target.ignore_pricing_rule = 1 target.flags.ignore_permissions = ignore_permissions target.run_method("set_missing_values") @@ -235,4 +235,4 @@ def _make_customer(source_name, ignore_permissions=False): else: return customer_name else: - return quotation[2] + return frappe.get_doc("Customer",quotation[2]) diff --git a/erpnext/shopping_cart/test_shopping_cart.py b/erpnext/shopping_cart/test_shopping_cart.py index d1124a662b6..adf57b85cdf 100644 --- a/erpnext/shopping_cart/test_shopping_cart.py +++ b/erpnext/shopping_cart/test_shopping_cart.py @@ -33,7 +33,6 @@ class TestShoppingCart(unittest.TestCase): self.assertEqual(quotation.quotation_to, "Customer") self.assertEqual(quotation.contact_person, frappe.db.get_value("Contact", dict(email_id="test_cart_user@example.com"))) - self.assertEqual(quotation.customer_lead, None) self.assertEqual(quotation.contact_email, frappe.session.user) return quotation From af4d588f648f4c16e2228ad25fd4fc20ad6aa06b Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sun, 14 Apr 2019 22:30:49 +0530 Subject: [PATCH 12/25] fix: Make customer and lead dynamic_link in opportunity --- erpnext/crm/doctype/lead/lead.py | 2 +- .../crm/doctype/opportunity/opportunity.js | 70 ++++++++----- .../crm/doctype/opportunity/opportunity.json | 99 ++++++++++--------- .../crm/doctype/opportunity/opportunity.py | 45 ++++----- .../doctype/opportunity/test_opportunity.py | 24 ++--- .../crm/doctype/opportunity/test_records.json | 4 +- .../move_customer_lead_to_dynamic_column.py | 6 +- erpnext/selling/doctype/customer/customer.py | 4 +- .../doctype/customer/customer_dashboard.py | 3 +- .../selling/doctype/quotation/quotation.js | 1 + 10 files changed, 144 insertions(+), 114 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index d45c105ab8e..d4460823b47 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -89,7 +89,7 @@ class Lead(SellingController): return frappe.db.get_value("Customer", {"lead_name": self.name}) def has_opportunity(self): - return frappe.db.get_value("Opportunity", {"lead": self.name, "status": ["!=", "Lost"]}) + return frappe.db.get_value("Opportunity", {"customer_lead": self.name, "status": ["!=", "Lost"]}) def has_quotation(self): return frappe.db.get_value("Quotation", { diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index abe560ba5ee..26975c552e2 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -9,16 +9,24 @@ frappe.ui.form.on("Opportunity", { frm.custom_make_buttons = { 'Quotation': 'Quotation', 'Supplier Quotation': 'Supplier Quotation' - } - }, - customer: function(frm) { - frm.trigger('set_contact_link'); - erpnext.utils.get_party_details(frm); - }, + }, - lead: function(frm) { - frm.trigger('set_contact_link'); + frm.set_query("opportunity_from", function() { + return{ + "filters": { + "name": ["in", ["Customer", "Lead"]], + } + } + }); }, + // customer: function(frm) { + // frm.trigger('set_contact_link'); + // erpnext.utils.get_party_details(frm); + // }, + + // lead: function(frm) { + // frm.trigger('set_contact_link'); + // }, with_items: function(frm) { frm.trigger('toggle_mandatory'); @@ -30,16 +38,17 @@ frappe.ui.form.on("Opportunity", { contact_person: erpnext.utils.get_contact_details, - enquiry_from: function(frm) { - frm.toggle_reqd("lead", frm.doc.enquiry_from==="Lead"); - frm.toggle_reqd("customer", frm.doc.enquiry_from==="Customer"); + opportunity_from: function(frm) { + frm.toggle_reqd("customer_lead", frm.doc.opportunity_from); + frm.trigger("set_dynamic_field_label"); }, refresh: function(frm) { var doc = frm.doc; - frm.events.enquiry_from(frm); - frm.trigger('set_contact_link'); + // frm.events.enquiry_from(frm); + // frm.trigger('set_contact_link'); frm.trigger('toggle_mandatory'); + frm.trigger("set_dynamic_field_label"); erpnext.toggle_naming_series(); if(!doc.__islocal && doc.status!=="Lost") { @@ -74,11 +83,22 @@ frappe.ui.form.on("Opportunity", { } }, - set_contact_link: function(frm) { - if(frm.doc.customer) { - frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} - } else if(frm.doc.lead) { - frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'} + // set_contact_link: function(frm) { + // if(frm.doc.customer) { + // frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} + // } else if(frm.doc.lead) { + // frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'} + // } + // }, + set_dynamic_field_label: function(frm){ + if (frm.doc.opportunity_from == "Customer") + { + frm.set_df_property("customer_lead", "label", "Customer"); + } + + if (frm.doc.opportunity_from == "Lead") + { + frm.set_df_property("customer_lead", "label", "Lead"); } }, @@ -97,10 +117,10 @@ frappe.ui.form.on("Opportunity", { // TODO commonify this code erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ onload: function() { - if(!this.frm.doc.enquiry_from && this.frm.doc.customer) - this.frm.doc.enquiry_from = "Customer"; - if(!this.frm.doc.enquiry_from && this.frm.doc.lead) - this.frm.doc.enquiry_from = "Lead"; + // if(!this.frm.doc.enquiry_from && this.frm.doc.customer) + // this.frm.doc.enquiry_from = "Customer"; + // if(!this.frm.doc.enquiry_from && this.frm.doc.lead) + // this.frm.doc.enquiry_from = "Lead"; if(!this.frm.doc.status) set_multiple(this.frm.doc.doctype, this.frm.doc.name, { status:'Open' }); @@ -148,7 +168,7 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ $.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm})); cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { - if(doc.enquiry_from == 'Lead' && doc.lead) + if(doc.opportunity_from == 'Lead' && doc.lead) cur_frm.cscript.lead(doc, cdt, cdn); } @@ -171,10 +191,10 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) { } cur_frm.cscript.lead = function(doc, cdt, cdn) { - cur_frm.toggle_display("contact_info", doc.customer || doc.lead); + cur_frm.toggle_display("contact_info", doc.customer_lead); erpnext.utils.map_current_doc({ method: "erpnext.crm.doctype.lead.lead.make_opportunity", - source_name: cur_frm.doc.lead, + source_name: cur_frm.doc.customer_lead, frm: cur_frm }); } diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index fc86842e01f..7e23a3c12bd 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -21,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "from_section", "fieldtype": "Section Break", "hidden": 0, @@ -54,6 +55,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", "hidden": 0, @@ -88,8 +90,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "enquiry_from", - "fieldtype": "Select", + "fetch_if_empty": 0, + "fieldname": "opportunity_from", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -102,7 +105,7 @@ "no_copy": 0, "oldfieldname": "enquiry_from", "oldfieldtype": "Select", - "options": "\nLead\nCustomer", + "options": "DocType", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -122,9 +125,10 @@ "bold": 1, "collapsible": 0, "columns": 0, - "depends_on": "eval:doc.enquiry_from===\"Customer\"", - "fieldname": "customer", - "fieldtype": "Link", + "depends_on": "", + "fetch_if_empty": 0, + "fieldname": "customer_lead", + "fieldtype": "Dynamic Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -132,47 +136,12 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 1, - "label": "Customer", + "label": "Customer/Lead", "length": 0, "no_copy": 0, "oldfieldname": "customer", "oldfieldtype": "Link", - "options": "Customer", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.enquiry_from===\"Lead\"", - "fieldname": "lead", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Lead", - "length": 0, - "no_copy": 0, - "oldfieldname": "lead", - "oldfieldtype": "Link", - "options": "Lead", + "options": "opportunity_from", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -193,6 +162,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "customer_name", "fieldtype": "Data", "hidden": 0, @@ -224,6 +194,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break0", "fieldtype": "Column Break", "hidden": 0, @@ -256,6 +227,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -289,6 +261,7 @@ "collapsible": 0, "columns": 0, "default": "Sales", + "fetch_if_empty": 0, "fieldname": "opportunity_type", "fieldtype": "Link", "hidden": 0, @@ -324,6 +297,7 @@ "collapsible": 0, "columns": 0, "default": "Open", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -359,6 +333,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.status===\"Lost\"", + "fetch_if_empty": 0, "fieldname": "order_lost_reason", "fieldtype": "Small Text", "hidden": 0, @@ -390,6 +365,7 @@ "bold": 1, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "mins_to_first_response", "fieldtype": "Float", "hidden": 0, @@ -423,6 +399,7 @@ "collapsible": 1, "collapsible_depends_on": "contact_by", "columns": 0, + "fetch_if_empty": 0, "fieldname": "next_contact", "fieldtype": "Section Break", "hidden": 0, @@ -456,6 +433,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "contact_by", "fieldtype": "Link", "hidden": 0, @@ -492,6 +470,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "contact_date", "fieldtype": "Datetime", "hidden": 0, @@ -525,6 +504,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break2", "fieldtype": "Column Break", "hidden": 0, @@ -557,6 +537,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "to_discuss", "fieldtype": "Small Text", "hidden": 0, @@ -590,6 +571,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_14", "fieldtype": "Section Break", "hidden": 0, @@ -622,6 +604,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "currency", "fieldtype": "Link", "hidden": 0, @@ -655,6 +638,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "opportunity_amount", "fieldtype": "Currency", "hidden": 0, @@ -687,6 +671,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "with_items", "fieldtype": "Check", "hidden": 0, @@ -719,6 +704,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_17", "fieldtype": "Column Break", "hidden": 0, @@ -751,6 +737,7 @@ "collapsible": 0, "columns": 0, "default": "Prospecting", + "fetch_if_empty": 0, "fieldname": "sales_stage", "fieldtype": "Link", "hidden": 0, @@ -785,6 +772,7 @@ "collapsible": 0, "columns": 0, "default": "100", + "fetch_if_empty": 0, "fieldname": "probability", "fieldtype": "Percent", "hidden": 0, @@ -818,6 +806,7 @@ "collapsible": 0, "columns": 0, "depends_on": "with_items", + "fetch_if_empty": 0, "fieldname": "items_section", "fieldtype": "Section Break", "hidden": 0, @@ -852,6 +841,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "items", "fieldtype": "Table", "hidden": 0, @@ -888,6 +878,7 @@ "collapsible_depends_on": "next_contact_by", "columns": 0, "depends_on": "eval:doc.lead || doc.customer", + "fetch_if_empty": 0, "fieldname": "contact_info", "fieldtype": "Section Break", "hidden": 0, @@ -921,6 +912,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.customer || doc.lead", + "fetch_if_empty": 0, "fieldname": "customer_address", "fieldtype": "Link", "hidden": 0, @@ -953,6 +945,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "address_display", "fieldtype": "Small Text", "hidden": 1, @@ -988,6 +981,7 @@ "columns": 0, "depends_on": "customer", "description": "", + "fetch_if_empty": 0, "fieldname": "territory", "fieldtype": "Link", "hidden": 0, @@ -1022,6 +1016,7 @@ "columns": 0, "depends_on": "customer", "description": "", + "fetch_if_empty": 0, "fieldname": "customer_group", "fieldtype": "Link", "hidden": 0, @@ -1056,6 +1051,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break3", "fieldtype": "Column Break", "hidden": 0, @@ -1087,6 +1083,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.lead || doc.customer", + "fetch_if_empty": 0, "fieldname": "contact_person", "fieldtype": "Link", "hidden": 0, @@ -1120,6 +1117,7 @@ "collapsible": 0, "columns": 0, "depends_on": "customer", + "fetch_if_empty": 0, "fieldname": "contact_display", "fieldtype": "Small Text", "hidden": 0, @@ -1152,6 +1150,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.lead || doc.customer", + "fetch_if_empty": 0, "fieldname": "contact_email", "fieldtype": "Data", "hidden": 0, @@ -1184,6 +1183,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.lead || doc.customer", + "fetch_if_empty": 0, "fieldname": "contact_mobile", "fieldtype": "Small Text", "hidden": 0, @@ -1216,6 +1216,7 @@ "collapsible": 1, "collapsible_depends_on": "", "columns": 0, + "fetch_if_empty": 0, "fieldname": "more_info", "fieldtype": "Section Break", "hidden": 0, @@ -1249,6 +1250,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "source", "fieldtype": "Link", "hidden": 0, @@ -1285,6 +1287,7 @@ "columns": 0, "depends_on": "eval: doc.source==\"Campaign\"", "description": "Enter name of campaign if source of enquiry is campaign", + "fetch_if_empty": 0, "fieldname": "campaign", "fieldtype": "Link", "hidden": 0, @@ -1319,6 +1322,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break1", "fieldtype": "Column Break", "hidden": 0, @@ -1351,6 +1355,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -1386,6 +1391,7 @@ "collapsible": 0, "columns": 0, "default": "Today", + "fetch_if_empty": 0, "fieldname": "transaction_date", "fieldtype": "Date", "hidden": 0, @@ -1420,6 +1426,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -1460,7 +1467,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-10-01 09:28:43.990999", + "modified": "2019-04-14 21:00:16.007097", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", @@ -1508,11 +1515,11 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, - "search_fields": "status,transaction_date,customer,lead,opportunity_type,territory,company", + "search_fields": "status,transaction_date,customer_lead,opportunity_type,territory,company", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "timeline_field": "customer", + "timeline_field": "customer_lead", "title_field": "title", "track_changes": 0, "track_seen": 1, diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 102d7378287..55fdbf577bd 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -15,8 +15,8 @@ sender_field = "contact_email" class Opportunity(TransactionBase): def after_insert(self): - if self.lead: - frappe.get_doc("Lead", self.lead).set_status(update=True) + if self.opportunity_from == "Lead": + frappe.get_doc("Lead", self.customer_lead).set_status(update=True) def validate(self): self._prev = frappe._dict({ @@ -28,7 +28,7 @@ class Opportunity(TransactionBase): self.make_new_lead_if_required() - if not self.enquiry_from: + if not self.opportunity_from: frappe.throw(_("Opportunity From field is mandatory")) self.validate_item_details() @@ -44,7 +44,7 @@ class Opportunity(TransactionBase): def make_new_lead_if_required(self): """Set lead against new opportunity""" - if not (self.lead or self.customer) and self.contact_email: + if not self.customer_lead and self.contact_email: # check if customer is already created agains the self.contact_email customer = frappe.db.sql("""select distinct `tabDynamic Link`.link_name as customer @@ -60,8 +60,8 @@ class Opportunity(TransactionBase): `tabDynamic Link`.link_doctype='Customer' """.format(self.contact_email), as_dict=True) if customer and customer[0].customer: - self.customer = customer[0].customer - self.enquiry_from = "Customer" + self.customer_lead = customer[0].customer + self.opportunity_from = "Customer" return lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email}) @@ -88,8 +88,8 @@ class Opportunity(TransactionBase): lead.insert(ignore_permissions=True) lead_name = lead.name - self.enquiry_from = "Lead" - self.lead = lead_name + self.opportunity_from = "Lead" + self.customer_lead = lead_name def declare_enquiry_lost(self,arg): if not self.has_active_quotation(): @@ -136,10 +136,10 @@ class Opportunity(TransactionBase): return True def validate_cust_name(self): - if self.customer: - self.customer_name = frappe.db.get_value("Customer", self.customer, "customer_name") - elif self.lead: - lead_name, company_name = frappe.db.get_value("Lead", self.lead, ["lead_name", "company_name"]) + if self.customer_lead and self.opportunity_from == 'Customer': + self.customer_name = frappe.db.get_value("Customer", self.customer_lead, "customer_name") + elif self.customer_lead and self.opportunity_from == 'Lead': + lead_name, company_name = frappe.db.get_value("Lead", self.customer_lead, ["lead_name", "company_name"]) self.customer_name = company_name or lead_name def on_update(self): @@ -152,16 +152,16 @@ class Opportunity(TransactionBase): opts.description = "" opts.contact_date = self.contact_date - if self.customer: + if self.customer_lead and self.opportunity_from == 'Customer': if self.contact_person: opts.description = 'Contact '+cstr(self.contact_person) else: - opts.description = 'Contact customer '+cstr(self.customer) - elif self.lead: + opts.description = 'Contact customer '+cstr(self.customer_lead) + elif self.customer_lead and self.opportunity_from == 'Lead': if self.contact_display: opts.description = 'Contact '+cstr(self.contact_display) else: - opts.description = 'Contact lead '+cstr(self.lead) + opts.description = 'Contact lead '+cstr(self.customer_lead) opts.subject = opts.description opts.description += '. By : ' + cstr(self.contact_by) @@ -187,16 +187,13 @@ class Opportunity(TransactionBase): if not d.get(key): d.set(key, item.get(key)) def validate_lead_cust(self): - if self.enquiry_from == 'Lead': - if not self.lead: + if self.opportunity_from == 'Lead': + if not self.customer_lead: frappe.throw(_("Lead must be set if Opportunity is made from Lead")) - else: - self.customer = None - elif self.enquiry_from == 'Customer': - if not self.customer: + elif self.opportunity_from == 'Customer': + if not self.customer_lead: msgprint(_("Customer is mandatory if 'Opportunity From' is selected as Customer"), raise_exception=1) - else: - self.lead = None + @frappe.whitelist() def get_item_details(item_code): diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index ef2945b41f9..1fc18bae24b 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -37,13 +37,13 @@ class TestOpportunity(unittest.TestCase): # new lead should be created against the new.opportunity@example.com opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) - self.assertTrue(opp_doc.lead) - self.assertEqual(opp_doc.enquiry_from, "Lead") - self.assertEqual(frappe.db.get_value("Lead", opp_doc.lead, "email_id"), + self.assertTrue(opp_doc.customer_lead) + self.assertEqual(opp_doc.opportunity_from, "Lead") + self.assertEqual(frappe.db.get_value("Lead", opp_doc.customer_lead, "email_id"), 'new.opportunity@example.com') # create new customer and create new contact against 'new.opportunity@example.com' - customer = make_customer(opp_doc.lead).insert(ignore_permissions=True) + customer = make_customer(opp_doc.customer_lead).insert(ignore_permissions=True) frappe.get_doc({ "doctype": "Contact", "email_id": "new.opportunity@example.com", @@ -55,9 +55,9 @@ class TestOpportunity(unittest.TestCase): }).insert(ignore_permissions=True) opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) - self.assertTrue(opp_doc.customer) - self.assertEqual(opp_doc.enquiry_from, "Customer") - self.assertEqual(opp_doc.customer, customer.name) + self.assertTrue(opp_doc.customer_lead) + self.assertEqual(opp_doc.opportunity_from, "Customer") + self.assertEqual(opp_doc.customer_lead, customer.name) def make_opportunity(**args): args = frappe._dict(args) @@ -65,17 +65,17 @@ def make_opportunity(**args): opp_doc = frappe.get_doc({ "doctype": "Opportunity", "company": args.company or "_Test Company", - "enquiry_from": args.enquiry_from or "Customer", + "opportunity_from": args.opportunity_from or "Customer", "opportunity_type": "Sales", "with_items": args.with_items or 0, "transaction_date": today() }) - if opp_doc.enquiry_from == 'Customer': - opp_doc.customer = args.customer or "_Test Customer" + if opp_doc.opportunity_from == 'Customer': + opp_doc.customer_lead= args.customer or "_Test Customer" - if opp_doc.enquiry_from == 'Lead': - opp_doc.customer = args.lead or "_T-Lead-00001" + if opp_doc.opportunity_from == 'Lead': + opp_doc.customer_lead = args.lead or "_T-Lead-00001" if args.with_items: opp_doc.append('items', { diff --git a/erpnext/crm/doctype/opportunity/test_records.json b/erpnext/crm/doctype/opportunity/test_records.json index 84dfea515a0..afabb764a1f 100644 --- a/erpnext/crm/doctype/opportunity/test_records.json +++ b/erpnext/crm/doctype/opportunity/test_records.json @@ -2,9 +2,9 @@ { "doctype": "Opportunity", "name": "_Test Opportunity 1", - "enquiry_from": "Lead", + "opportunity_from": "Lead", "enquiry_type": "Sales", - "lead": "_T-Lead-00001", + "customer_lead": "_T-Lead-00001", "transaction_date": "2013-12-12", "items": [{ "item_name": "Test Item", diff --git a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py index 345b4ac714a..33eb713cc51 100644 --- a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py +++ b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py @@ -7,4 +7,8 @@ import frappe def execute(): frappe.reload_doctype("Quotation") frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = lead WHERE quotation_to = 'Lead' """) - frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = customer WHERE quotation_to = 'Customer' """) \ No newline at end of file + frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = customer WHERE quotation_to = 'Customer' """) + + frappe.reload_doctype("Opportunity") + frappe.db.sql(""" UPDATE `tabOpportunity` set customer_lead = lead WHERE opportunity_from = 'Lead' """) + frappe.db.sql(""" UPDATE `tabOpportunity` set customer_lead = customer WHERE opportunity_from = 'Customer' """) \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 139d76157e7..968b6faed39 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -104,8 +104,8 @@ class Customer(TransactionBase): if self.lead_name: frappe.db.set_value('Lead', self.lead_name, 'status', 'Converted', update_modified=False) - for d in frappe.get_all('Opportunity', {'lead': self.lead_name}): - frappe.db.set_value('Opportunity', d.name, 'customer', self.name, update_modified=False) + for d in frappe.get_all('Opportunity', {'customer_lead': self.lead_name}): + frappe.db.set_value('Opportunity', d.name, 'customer_lead', self.name, update_modified=False) def create_lead_address_contact(self): if self.lead_name: diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py index b7175c9675d..f8dbbd6d4ec 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -7,7 +7,8 @@ def get_data(): 'heatmap_message': _('This is based on transactions against this Customer. See timeline below for details'), 'fieldname': 'customer', 'non_standard_fieldnames': { - 'Quotation': 'customer_lead' + 'Quotation': 'customer_lead', + 'Opportunity': 'customer_lead' }, 'transactions': [ { diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index cf8788963cc..5455caa069b 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -28,6 +28,7 @@ frappe.ui.form.on('Quotation', { quotation_to: function(frm) { frm.trigger("set_label"); frm.trigger("toggle_reqd_lead_customer"); + frm.trigger("set_dynamic_field_label"); }, set_label: function(frm) { From e8883e20cd9d4e3146fa0f56096412d417db435d Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Sun, 14 Apr 2019 22:39:58 +0530 Subject: [PATCH 13/25] fix: Remove comments and unused code --- .../crm/doctype/opportunity/opportunity.js | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 26975c552e2..e70745e8f51 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -19,14 +19,6 @@ frappe.ui.form.on("Opportunity", { } }); }, - // customer: function(frm) { - // frm.trigger('set_contact_link'); - // erpnext.utils.get_party_details(frm); - // }, - - // lead: function(frm) { - // frm.trigger('set_contact_link'); - // }, with_items: function(frm) { frm.trigger('toggle_mandatory'); @@ -46,7 +38,6 @@ frappe.ui.form.on("Opportunity", { refresh: function(frm) { var doc = frm.doc; // frm.events.enquiry_from(frm); - // frm.trigger('set_contact_link'); frm.trigger('toggle_mandatory'); frm.trigger("set_dynamic_field_label"); erpnext.toggle_naming_series(); @@ -83,13 +74,6 @@ frappe.ui.form.on("Opportunity", { } }, - // set_contact_link: function(frm) { - // if(frm.doc.customer) { - // frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} - // } else if(frm.doc.lead) { - // frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'} - // } - // }, set_dynamic_field_label: function(frm){ if (frm.doc.opportunity_from == "Customer") { @@ -117,10 +101,6 @@ frappe.ui.form.on("Opportunity", { // TODO commonify this code erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ onload: function() { - // if(!this.frm.doc.enquiry_from && this.frm.doc.customer) - // this.frm.doc.enquiry_from = "Customer"; - // if(!this.frm.doc.enquiry_from && this.frm.doc.lead) - // this.frm.doc.enquiry_from = "Lead"; if(!this.frm.doc.status) set_multiple(this.frm.doc.doctype, this.frm.doc.name, { status:'Open' }); @@ -168,7 +148,7 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ $.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm})); cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { - if(doc.opportunity_from == 'Lead' && doc.lead) + if(doc.opportunity_from == 'Lead' && doc.customer_lead) cur_frm.cscript.lead(doc, cdt, cdn); } From f0ee3d26a7820901bb667b928130582884216ae8 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 15 Apr 2019 19:20:59 +0530 Subject: [PATCH 14/25] fix: Styling and indentation fixes --- .../crm/doctype/opportunity/opportunity.py | 2 +- .../doctype/quotation/test_records.json | 70 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 55fdbf577bd..72851d98fcc 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -44,7 +44,7 @@ class Opportunity(TransactionBase): def make_new_lead_if_required(self): """Set lead against new opportunity""" - if not self.customer_lead and self.contact_email: + if not self.customer_lead and self.contact_email: #nosec # check if customer is already created agains the self.contact_email customer = frappe.db.sql("""select distinct `tabDynamic Link`.link_name as customer diff --git a/erpnext/selling/doctype/quotation/test_records.json b/erpnext/selling/doctype/quotation/test_records.json index 03acea35cfa..e18798926ba 100644 --- a/erpnext/selling/doctype/quotation/test_records.json +++ b/erpnext/selling/doctype/quotation/test_records.json @@ -1,37 +1,37 @@ [ - { - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "customer_lead": "_Test Customer", - "customer_group": "_Test Customer Group", - "customer_name": "_Test Customer", - "doctype": "Quotation", - "base_grand_total": 1000.0, - "grand_total": 1000.0, - "order_type": "Sales", - "plc_conversion_rate": 1.0, - "price_list_currency": "INR", - "items": [ - { - "base_amount": 1000.0, - "base_rate": 100.0, - "description": "CPU", - "doctype": "Quotation Item", - "item_code": "_Test Item Home Desktop 100", - "item_name": "CPU", - "parentfield": "items", - "qty": 10.0, - "rate": 100.0, - "uom": "_Test UOM 1", - "stock_uom": "_Test UOM 1", - "conversion_factor": 1.0 - } - ], - "quotation_to": "Customer", - "selling_price_list": "_Test Price List", - "territory": "_Test Territory", - "transaction_date": "2013-02-21", - "valid_till": "2013-03-21" - } + { + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "customer_lead": "_Test Customer", + "customer_group": "_Test Customer Group", + "customer_name": "_Test Customer", + "doctype": "Quotation", + "base_grand_total": 1000.0, + "grand_total": 1000.0, + "order_type": "Sales", + "plc_conversion_rate": 1.0, + "price_list_currency": "INR", + "items": [ + { + "base_amount": 1000.0, + "base_rate": 100.0, + "description": "CPU", + "doctype": "Quotation Item", + "item_code": "_Test Item Home Desktop 100", + "item_name": "CPU", + "parentfield": "items", + "qty": 10.0, + "rate": 100.0, + "uom": "_Test UOM 1", + "stock_uom": "_Test UOM 1", + "conversion_factor": 1.0 + } + ], + "quotation_to": "Customer", + "selling_price_list": "_Test Price List", + "territory": "_Test Territory", + "transaction_date": "2013-02-21", + "valid_till": "2013-03-21" + } ] \ No newline at end of file From d333d2e6ebfcc6def9961d6e7fb82b3e6d0897eb Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 16 Apr 2019 08:53:29 +0530 Subject: [PATCH 15/25] fix: Change enquiry_from to opportunity_from in multiple files --- erpnext/crm/doctype/opportunity/opportunity.js | 3 +-- erpnext/crm/doctype/opportunity/opportunity.py | 4 ++-- erpnext/crm/doctype/opportunity/opportunity_list.js | 2 +- erpnext/crm/doctype/opportunity/test_opportunity.js | 2 +- .../crm/report/lead_conversion_time/lead_conversion_time.py | 2 +- erpnext/demo/user/sales.py | 2 +- erpnext/hub_node/legacy.py | 2 +- erpnext/setup/setup_wizard/operations/sample_data.py | 2 +- erpnext/templates/utils.py | 2 +- 9 files changed, 10 insertions(+), 11 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index e70745e8f51..1afea839507 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -37,9 +37,8 @@ frappe.ui.form.on("Opportunity", { refresh: function(frm) { var doc = frm.doc; - // frm.events.enquiry_from(frm); + frm.events.opportunity_from(frm); frm.trigger('toggle_mandatory'); - frm.trigger("set_dynamic_field_label"); erpnext.toggle_naming_series(); if(!doc.__islocal && doc.status!=="Lost") { diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 72851d98fcc..8d7479fd896 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -44,7 +44,7 @@ class Opportunity(TransactionBase): def make_new_lead_if_required(self): """Set lead against new opportunity""" - if not self.customer_lead and self.contact_email: #nosec + if (not self.get("customer_lead")) and self.contact_email: # check if customer is already created agains the self.contact_email customer = frappe.db.sql("""select distinct `tabDynamic Link`.link_name as customer @@ -242,7 +242,7 @@ def make_quotation(source_name, target_doc=None): "Opportunity": { "doctype": "Quotation", "field_map": { - "enquiry_from": "quotation_to", + "opportunity_from": "quotation_to", "opportunity_type": "order_type", "name": "enq_no", } diff --git a/erpnext/crm/doctype/opportunity/opportunity_list.js b/erpnext/crm/doctype/opportunity/opportunity_list.js index 0dbbf8add1c..9712fb04c56 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_list.js +++ b/erpnext/crm/doctype/opportunity/opportunity_list.js @@ -1,5 +1,5 @@ frappe.listview_settings['Opportunity'] = { - add_fields: ["customer_name", "opportunity_type", "enquiry_from", "status"], + add_fields: ["customer_name", "opportunity_type", "opportunity_from", "status"], get_indicator: function(doc) { var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status]; if(doc.status=="Quotation") { diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.js b/erpnext/crm/doctype/opportunity/test_opportunity.js index f2b04f86474..45b97ddc4d1 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.js +++ b/erpnext/crm/doctype/opportunity/test_opportunity.js @@ -6,7 +6,7 @@ QUnit.test("test: opportunity", function (assert) { () => frappe.timeout(1), () => frappe.click_button('New'), () => frappe.timeout(1), - () => cur_frm.set_value('enquiry_from', 'Customer'), + () => cur_frm.set_value('opportunity_from', 'Customer'), () => cur_frm.set_value('customer', 'Test Customer 1'), // check items diff --git a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py index d9ee30ec1ae..d91b9c5607d 100644 --- a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py +++ b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py @@ -66,7 +66,7 @@ def get_columns(): def get_communication_details(filters): communication_count = None communication_list = [] - opportunities = frappe.db.get_values('Opportunity', {'enquiry_from': 'Lead'},\ + opportunities = frappe.db.get_values('Opportunity', {'opportunity_from': 'Lead'},\ ['name', 'customer_name', 'lead', 'contact_email'], as_dict=1) for d in opportunities: diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py index 69ba9007a61..3809c1f0924 100644 --- a/erpnext/demo/user/sales.py +++ b/erpnext/demo/user/sales.py @@ -56,7 +56,7 @@ def work(domain="Manufacturing"): def make_opportunity(domain): b = frappe.get_doc({ "doctype": "Opportunity", - "enquiry_from": "Customer", + "opportunity_from": "Customer", "customer": get_random("Customer"), "opportunity_type": "Sales", "with_items": 1, diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py index 9daee2752fa..95ada76a6a5 100644 --- a/erpnext/hub_node/legacy.py +++ b/erpnext/hub_node/legacy.py @@ -28,7 +28,7 @@ def make_opportunity(buyer_name, email_id): lead.save(ignore_permissions=True) o = frappe.new_doc("Opportunity") - o.enquiry_from = "Lead" + o.opportunity_from = "Lead" o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"] o.save(ignore_permissions=True) diff --git a/erpnext/setup/setup_wizard/operations/sample_data.py b/erpnext/setup/setup_wizard/operations/sample_data.py index 3f787347393..e21c9bd1089 100644 --- a/erpnext/setup/setup_wizard/operations/sample_data.py +++ b/erpnext/setup/setup_wizard/operations/sample_data.py @@ -33,7 +33,7 @@ def make_sample_data(domains, make_dependent = False): def make_opportunity(items, customer): b = frappe.get_doc({ "doctype": "Opportunity", - "enquiry_from": "Customer", + "opportunity_from": "Customer", "customer": customer, "opportunity_type": _("Sales"), "with_items": 1 diff --git a/erpnext/templates/utils.py b/erpnext/templates/utils.py index cb44fd30d22..97e2a7f7235 100644 --- a/erpnext/templates/utils.py +++ b/erpnext/templates/utils.py @@ -28,7 +28,7 @@ def send_message(subject="Website Query", message="", sender="", status="Open"): opportunity = frappe.get_doc(dict( doctype ='Opportunity', - enquiry_from = 'Customer' if customer else 'Lead', + opportunity_from = 'Customer' if customer else 'Lead', status = 'Open', title = subject, contact_email = sender, From 28fe73640b629aefe8b3b88d10288610ed36fab6 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 16 Apr 2019 15:21:51 +0530 Subject: [PATCH 16/25] fix: Dynamic link fixes in quotation and opportunity --- erpnext/crm/doctype/opportunity/opportunity.js | 15 +++++++++++++++ erpnext/selling/doctype/quotation/quotation.js | 2 ++ 2 files changed, 17 insertions(+) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 1afea839507..655427ea08b 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -20,6 +20,13 @@ frappe.ui.form.on("Opportunity", { }); }, + customer_lead: function(frm) { + if (frm.doc.opportunity_from == "Customer") { + frm.trigger('set_contact_link'); + erpnext.utils.get_party_details(frm); + } + }, + with_items: function(frm) { frm.trigger('toggle_mandatory'); }, @@ -73,6 +80,14 @@ frappe.ui.form.on("Opportunity", { } }, + set_contact_link: function(frm) { + if(frm.doc.opportunity_from == "Customer" && frm.doc.customer_lead) { + frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} + } else if(frm.doc.opportunity_from == "Lead" && frm.doc.customer_lead) { + frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'} + } + }, + set_dynamic_field_label: function(frm){ if (frm.doc.opportunity_from == "Customer") { diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 5455caa069b..257f9919c88 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -44,6 +44,8 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ }, refresh: function(doc, dt, dn) { this._super(doc, dt, dn); + doctype = doc.quotation_to == 'Customer' ? 'Customer':'Lead'; + frappe.dynamic_link = {doc: this.frm.doc, fieldname: doctype.toLowerCase(), doctype: doctype} var me = this; From 3d31bccaf63cf1d474615780744b2a2e6dd69a8a Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 25 Apr 2019 17:29:21 +0530 Subject: [PATCH 17/25] fix: Change dynamic_field name and minor fixes --- erpnext/crm/doctype/lead/lead.py | 4 +- .../crm/doctype/opportunity/opportunity.js | 23 ++++------ .../crm/doctype/opportunity/opportunity.json | 11 ++--- .../crm/doctype/opportunity/opportunity.py | 42 ++++++++----------- .../doctype/opportunity/test_opportunity.py | 14 +++---- .../crm/doctype/opportunity/test_records.json | 2 +- .../move_customer_lead_to_dynamic_column.py | 8 ++-- erpnext/selling/doctype/customer/customer.py | 3 -- .../doctype/customer/customer_dashboard.py | 4 +- .../selling/doctype/quotation/quotation.js | 10 ++--- .../selling/doctype/quotation/quotation.json | 8 ++-- .../selling/doctype/quotation/quotation.py | 6 +-- .../doctype/quotation/test_quotation.py | 10 ++--- .../doctype/quotation/test_records.json | 2 +- erpnext/shopping_cart/cart.py | 12 +++--- erpnext/shopping_cart/test_shopping_cart.py | 6 +-- 16 files changed, 75 insertions(+), 90 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index b80c43f1ff5..d3d31b8e702 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -90,11 +90,11 @@ class Lead(SellingController): return frappe.db.get_value("Customer", {"lead_name": self.name}) def has_opportunity(self): - return frappe.db.get_value("Opportunity", {"customer_lead": self.name, "status": ["!=", "Lost"]}) + return frappe.db.get_value("Opportunity", {"party_name": self.name, "status": ["!=", "Lost"]}) def has_quotation(self): return frappe.db.get_value("Quotation", { - "customer_lead": self.name, + "party_name": self.name, "docstatus": 1, "status": ["!=", "Lost"] diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 655427ea08b..88ce10c9561 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -20,7 +20,7 @@ frappe.ui.form.on("Opportunity", { }); }, - customer_lead: function(frm) { + party_name: function(frm) { if (frm.doc.opportunity_from == "Customer") { frm.trigger('set_contact_link'); erpnext.utils.get_party_details(frm); @@ -38,7 +38,7 @@ frappe.ui.form.on("Opportunity", { contact_person: erpnext.utils.get_contact_details, opportunity_from: function(frm) { - frm.toggle_reqd("customer_lead", frm.doc.opportunity_from); + frm.toggle_reqd("party_name", frm.doc.opportunity_from); frm.trigger("set_dynamic_field_label"); }, @@ -81,22 +81,17 @@ frappe.ui.form.on("Opportunity", { }, set_contact_link: function(frm) { - if(frm.doc.opportunity_from == "Customer" && frm.doc.customer_lead) { + if(frm.doc.opportunity_from == "Customer" && frm.doc.party_name) { frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} - } else if(frm.doc.opportunity_from == "Lead" && frm.doc.customer_lead) { + } else if(frm.doc.opportunity_from == "Lead" && frm.doc.party_name) { frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'} } }, set_dynamic_field_label: function(frm){ - if (frm.doc.opportunity_from == "Customer") - { - frm.set_df_property("customer_lead", "label", "Customer"); - } - if (frm.doc.opportunity_from == "Lead") - { - frm.set_df_property("customer_lead", "label", "Lead"); + if (frm.doc.opportunity_from) { + frm.set_df_property("party_name", "label", frm.doc.opportunity_from); } }, @@ -162,7 +157,7 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ $.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm})); cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { - if(doc.opportunity_from == 'Lead' && doc.customer_lead) + if(doc.opportunity_from == 'Lead' && doc.party_name) cur_frm.cscript.lead(doc, cdt, cdn); } @@ -185,10 +180,10 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) { } cur_frm.cscript.lead = function(doc, cdt, cdn) { - cur_frm.toggle_display("contact_info", doc.customer_lead); + cur_frm.toggle_display("contact_info", doc.party_name); erpnext.utils.map_current_doc({ method: "erpnext.crm.doctype.lead.lead.make_opportunity", - source_name: cur_frm.doc.customer_lead, + source_name: cur_frm.doc.party_name, frm: cur_frm }); } diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 7e23a3c12bd..4026ff77ddb 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -127,7 +127,7 @@ "columns": 0, "depends_on": "", "fetch_if_empty": 0, - "fieldname": "customer_lead", + "fieldname": "party_name", "fieldtype": "Dynamic Link", "hidden": 0, "ignore_user_permissions": 0, @@ -148,7 +148,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -162,6 +162,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_from": "party_name.customer_name", "fetch_if_empty": 0, "fieldname": "customer_name", "fieldtype": "Data", @@ -1467,7 +1468,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-04-14 21:00:16.007097", + "modified": "2019-04-25 17:10:09.399721", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", @@ -1515,11 +1516,11 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, - "search_fields": "status,transaction_date,customer_lead,opportunity_type,territory,company", + "search_fields": "status,transaction_date,party_name,opportunity_type,territory,company", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "timeline_field": "customer_lead", + "timeline_field": "party_name", "title_field": "title", "track_changes": 0, "track_seen": 1, diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 13337e1657e..ed58821cbbd 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -17,7 +17,7 @@ sender_field = "contact_email" class Opportunity(TransactionBase): def after_insert(self): if self.opportunity_from == "Lead": - frappe.get_doc("Lead", self.customer_lead).set_status(update=True) + frappe.get_doc("Lead", self.party_name).set_status(update=True) def validate(self): self._prev = frappe._dict({ @@ -29,9 +29,6 @@ class Opportunity(TransactionBase): self.make_new_lead_if_required() - if not self.opportunity_from: - frappe.throw(_("Opportunity From field is mandatory")) - self.validate_item_details() self.validate_uom_is_integer("uom", "qty") self.validate_lead_cust() @@ -45,7 +42,7 @@ class Opportunity(TransactionBase): def make_new_lead_if_required(self): """Set lead against new opportunity""" - if (not self.get("customer_lead")) and self.contact_email: + if (not self.get("party_name")) and self.contact_email: # check if customer is already created agains the self.contact_email customer = frappe.db.sql("""select distinct `tabDynamic Link`.link_name as customer @@ -61,7 +58,7 @@ class Opportunity(TransactionBase): `tabDynamic Link`.link_doctype='Customer' """.format(self.contact_email), as_dict=True) if customer and customer[0].customer: - self.customer_lead = customer[0].customer + self.party_name = customer[0].customer self.opportunity_from = "Customer" return @@ -90,7 +87,7 @@ class Opportunity(TransactionBase): lead_name = lead.name self.opportunity_from = "Lead" - self.customer_lead = lead_name + self.party_name = lead_name def declare_enquiry_lost(self,arg): if not self.has_active_quotation(): @@ -137,10 +134,10 @@ class Opportunity(TransactionBase): return True def validate_cust_name(self): - if self.customer_lead and self.opportunity_from == 'Customer': - self.customer_name = frappe.db.get_value("Customer", self.customer_lead, "customer_name") - elif self.customer_lead and self.opportunity_from == 'Lead': - lead_name, company_name = frappe.db.get_value("Lead", self.customer_lead, ["lead_name", "company_name"]) + if self.party_name and self.opportunity_from == 'Customer': + self.customer_name = frappe.db.get_value("Customer", self.party_name, "customer_name") + elif self.party_name and self.opportunity_from == 'Lead': + lead_name, company_name = frappe.db.get_value("Lead", self.party_name, ["lead_name", "company_name"]) self.customer_name = company_name or lead_name def on_update(self): @@ -153,16 +150,16 @@ class Opportunity(TransactionBase): opts.description = "" opts.contact_date = self.contact_date - if self.customer_lead and self.opportunity_from == 'Customer': + if self.party_name and self.opportunity_from == 'Customer': if self.contact_person: opts.description = 'Contact '+cstr(self.contact_person) else: - opts.description = 'Contact customer '+cstr(self.customer_lead) - elif self.customer_lead and self.opportunity_from == 'Lead': + opts.description = 'Contact customer '+cstr(self.party_name) + elif self.party_name and self.opportunity_from == 'Lead': if self.contact_display: opts.description = 'Contact '+cstr(self.contact_display) else: - opts.description = 'Contact lead '+cstr(self.customer_lead) + opts.description = 'Contact lead '+cstr(self.party_name) opts.subject = opts.description opts.description += '. By : ' + cstr(self.contact_by) @@ -187,14 +184,6 @@ class Opportunity(TransactionBase): for key in item_fields: if not d.get(key): d.set(key, item.get(key)) - def validate_lead_cust(self): - if self.opportunity_from == 'Lead': - if not self.customer_lead: - frappe.throw(_("Lead must be set if Opportunity is made from Lead")) - elif self.opportunity_from == 'Customer': - if not self.customer_lead: - msgprint(_("Customer is mandatory if 'Opportunity From' is selected as Customer"), raise_exception=1) - @frappe.whitelist() def get_item_details(item_code): @@ -216,8 +205,11 @@ def make_quotation(source_name, target_doc=None): quotation = frappe.get_doc(target) company_currency = frappe.get_cached_value('Company', quotation.company, "default_currency") - party_account_currency = get_party_account_currency("Customer", quotation.customer_lead, - quotation.company) if quotation.customer_lead else company_currency + + if quotation.quotation_to == 'Customer' and quotation.party_name: + party_account_currency = get_party_account_currency("Customer", quotation.party_name, quotation.company) + else: + party_account_currency = company_currency quotation.currency = party_account_currency or company_currency diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 1fc18bae24b..61dac90cd9f 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -37,13 +37,13 @@ class TestOpportunity(unittest.TestCase): # new lead should be created against the new.opportunity@example.com opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) - self.assertTrue(opp_doc.customer_lead) + self.assertTrue(opp_doc.party_name) self.assertEqual(opp_doc.opportunity_from, "Lead") - self.assertEqual(frappe.db.get_value("Lead", opp_doc.customer_lead, "email_id"), + self.assertEqual(frappe.db.get_value("Lead", opp_doc.party_name, "email_id"), 'new.opportunity@example.com') # create new customer and create new contact against 'new.opportunity@example.com' - customer = make_customer(opp_doc.customer_lead).insert(ignore_permissions=True) + customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True) frappe.get_doc({ "doctype": "Contact", "email_id": "new.opportunity@example.com", @@ -55,9 +55,9 @@ class TestOpportunity(unittest.TestCase): }).insert(ignore_permissions=True) opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) - self.assertTrue(opp_doc.customer_lead) + self.assertTrue(opp_doc.party_name) self.assertEqual(opp_doc.opportunity_from, "Customer") - self.assertEqual(opp_doc.customer_lead, customer.name) + self.assertEqual(opp_doc.party_name, customer.name) def make_opportunity(**args): args = frappe._dict(args) @@ -72,10 +72,10 @@ def make_opportunity(**args): }) if opp_doc.opportunity_from == 'Customer': - opp_doc.customer_lead= args.customer or "_Test Customer" + opp_doc.party_name= args.customer or "_Test Customer" if opp_doc.opportunity_from == 'Lead': - opp_doc.customer_lead = args.lead or "_T-Lead-00001" + opp_doc.party_name = args.lead or "_T-Lead-00001" if args.with_items: opp_doc.append('items', { diff --git a/erpnext/crm/doctype/opportunity/test_records.json b/erpnext/crm/doctype/opportunity/test_records.json index afabb764a1f..a1e0ad921b4 100644 --- a/erpnext/crm/doctype/opportunity/test_records.json +++ b/erpnext/crm/doctype/opportunity/test_records.json @@ -4,7 +4,7 @@ "name": "_Test Opportunity 1", "opportunity_from": "Lead", "enquiry_type": "Sales", - "customer_lead": "_T-Lead-00001", + "party_name": "_T-Lead-00001", "transaction_date": "2013-12-12", "items": [{ "item_name": "Test Item", diff --git a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py index 33eb713cc51..5b1251c31cf 100644 --- a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py +++ b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py @@ -6,9 +6,9 @@ import frappe def execute(): frappe.reload_doctype("Quotation") - frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = lead WHERE quotation_to = 'Lead' """) - frappe.db.sql(""" UPDATE `tabQuotation` set customer_lead = customer WHERE quotation_to = 'Customer' """) + frappe.db.sql(""" UPDATE `tabQuotation` set party_name = lead WHERE quotation_to = 'Lead' """) + frappe.db.sql(""" UPDATE `tabQuotation` set party_name = customer WHERE quotation_to = 'Customer' """) frappe.reload_doctype("Opportunity") - frappe.db.sql(""" UPDATE `tabOpportunity` set customer_lead = lead WHERE opportunity_from = 'Lead' """) - frappe.db.sql(""" UPDATE `tabOpportunity` set customer_lead = customer WHERE opportunity_from = 'Customer' """) \ No newline at end of file + frappe.db.sql(""" UPDATE `tabOpportunity` set party_name = lead WHERE opportunity_from = 'Lead' """) + frappe.db.sql(""" UPDATE `tabOpportunity` set party_name = customer WHERE opportunity_from = 'Customer' """) \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 968b6faed39..f815e5f97cb 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -104,9 +104,6 @@ class Customer(TransactionBase): if self.lead_name: frappe.db.set_value('Lead', self.lead_name, 'status', 'Converted', update_modified=False) - for d in frappe.get_all('Opportunity', {'customer_lead': self.lead_name}): - frappe.db.set_value('Opportunity', d.name, 'customer_lead', self.name, update_modified=False) - def create_lead_address_contact(self): if self.lead_name: # assign lead address to customer (if already not set) diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py index f8dbbd6d4ec..6d6c86fbefd 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -7,8 +7,8 @@ def get_data(): 'heatmap_message': _('This is based on transactions against this Customer. See timeline below for details'), 'fieldname': 'customer', 'non_standard_fieldnames': { - 'Quotation': 'customer_lead', - 'Opportunity': 'customer_lead' + 'Quotation': 'party_name', + 'Opportunity': 'party_name' }, 'transactions': [ { diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 257f9919c88..3116e65b686 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -108,15 +108,15 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ set_dynamic_field_label: function(){ if (this.frm.doc.quotation_to == "Customer") { - this.frm.set_df_property("customer_lead", "label", "Customer"); - this.frm.fields_dict.customer_lead.get_query = null; + this.frm.set_df_property("party_name", "label", "Customer"); + this.frm.fields_dict.party_name.get_query = null; } if (this.frm.doc.quotation_to == "Lead") { - this.frm.set_df_property("customer_lead", "label", "Lead"); + this.frm.set_df_property("party_name", "label", "Lead"); - this.frm.fields_dict.customer_lead.get_query = function() { + this.frm.fields_dict.party_name.get_query = function() { return{ query: "erpnext.controllers.queries.lead_query" } } } @@ -126,7 +126,7 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ var me = this; // to overwrite the customer_filter trigger from queries.js - this.frm.toggle_reqd("customer_lead", this.frm.doc.quotation_to); + this.frm.toggle_reqd("party_name", this.frm.doc.quotation_to); this.frm.set_query('customer_address', erpnext.queries.address_query); this.frm.set_query('shipping_address_name', erpnext.queries.address_query); }, diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 87a8aab3cfc..4b247075451 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -161,7 +161,7 @@ "columns": 0, "depends_on": "", "fetch_if_empty": 0, - "fieldname": "customer_lead", + "fieldname": "party_name", "fieldtype": "Dynamic Link", "hidden": 0, "ignore_user_permissions": 0, @@ -3224,7 +3224,7 @@ "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2019-04-02 10:18:58.506125", + "modified": "2019-04-25 15:26:21.983298", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", @@ -3390,11 +3390,11 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 1, - "search_fields": "status,transaction_date,customer_lead,order_type", + "search_fields": "status,transaction_date,party_name,order_type", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "timeline_field": "customer_lead", + "timeline_field": "party_name", "title_field": "title", "track_changes": 0, "track_seen": 0, diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index e99b635af9b..42b68728ad2 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -43,8 +43,8 @@ class Quotation(SellingController): super(Quotation, self).validate_order_type() def update_lead(self): - if self.quotation_to == "Lead" and self.customer_lead: - frappe.get_doc("Lead", self.customer_lead).set_status(update=True) + if self.quotation_to == "Lead" and self.party_name: + frappe.get_doc("Lead", self.party_name).set_status(update=True) def update_opportunity(self): for opportunity in list(set([d.prevdoc_docname for d in self.get("items")])): @@ -204,7 +204,7 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): return doclist def _make_customer(source_name, ignore_permissions=False): - quotation = frappe.db.get_value("Quotation", source_name, ["order_type", "customer_lead", "customer_name"]) + quotation = frappe.db.get_value("Quotation", source_name, ["order_type", "party_name", "customer_name"]) if quotation and quotation[1] and not quotation[2]: lead_name = quotation[1] customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name}, diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 124c7922a80..8bb8c618f29 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -203,15 +203,15 @@ class TestQuotation(unittest.TestCase): test_records = frappe.get_test_records('Quotation') -def get_quotation_dict(customer_lead=None, item_code=None): - if not customer_lead: - customer_lead = '_Test Customer' +def get_quotation_dict(party_name=None, item_code=None): + if not party_name: + party_name = '_Test Customer' if not item_code: item_code = '_Test Item' return { 'doctype': 'Quotation', - 'customer_lead': customer_lead, + 'party_name': party_name, 'items': [ { 'item_code': item_code, @@ -229,7 +229,7 @@ def make_quotation(**args): qo.transaction_date = args.transaction_date qo.company = args.company or "_Test Company" - qo.customer_lead = args.customer_lead or "_Test Customer" + qo.party_name = args.party_name or "_Test Customer" qo.currency = args.currency or "INR" if args.selling_price_list: qo.selling_price_list = args.selling_price_list diff --git a/erpnext/selling/doctype/quotation/test_records.json b/erpnext/selling/doctype/quotation/test_records.json index e18798926ba..1564f7de0ce 100644 --- a/erpnext/selling/doctype/quotation/test_records.json +++ b/erpnext/selling/doctype/quotation/test_records.json @@ -3,7 +3,7 @@ "company": "_Test Company", "conversion_rate": 1.0, "currency": "INR", - "customer_lead": "_Test Customer", + "party_name": "_Test Customer", "customer_group": "_Test Customer Group", "customer_name": "_Test Customer", "doctype": "Quotation", diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 59539e62572..efc128aa0c5 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -196,7 +196,7 @@ def _get_cart_quotation(party=None): party = get_party() quotation = frappe.get_all("Quotation", fields=["name"], filters= - {"customer_lead": party.name, "order_type": "Shopping Cart", "docstatus": 0}, + {"party_name": party.name, "order_type": "Shopping Cart", "docstatus": 0}, order_by="modified desc", limit_page_length=1) if quotation: @@ -211,7 +211,7 @@ def _get_cart_quotation(party=None): "status": "Draft", "docstatus": 0, "__islocal": 1, - "customer_lead": party.name + "party_name": party.name }) qdoc.contact_person = frappe.db.get_value("Contact", {"email_id": frappe.session.user}) @@ -291,9 +291,9 @@ def _set_price_list(quotation, cart_settings): # check if customer price list exists selling_price_list = None - if quotation.customer_lead: + if quotation.party_name: from erpnext.accounts.party import get_default_price_list - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.customer_lead)) + selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) # else check for territory based price list if not selling_price_list: @@ -305,9 +305,9 @@ def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" from erpnext.accounts.party import set_taxes - customer_group = frappe.db.get_value("Customer", quotation.customer_lead, "customer_group") + customer_group = frappe.db.get_value("Customer", quotation.party_name, "customer_group") - quotation.taxes_and_charges = set_taxes(quotation.customer_lead, "Customer", \ + quotation.taxes_and_charges = set_taxes(quotation.party_name, "Customer", \ quotation.transaction_date, quotation.company, customer_group, None, \ quotation.customer_address, quotation.shipping_address_name, 1) # diff --git a/erpnext/shopping_cart/test_shopping_cart.py b/erpnext/shopping_cart/test_shopping_cart.py index adf57b85cdf..be08ec44445 100644 --- a/erpnext/shopping_cart/test_shopping_cart.py +++ b/erpnext/shopping_cart/test_shopping_cart.py @@ -43,7 +43,7 @@ class TestShoppingCart(unittest.TestCase): # test if quotation with customer is fetched quotation = _get_cart_quotation() self.assertEqual(quotation.quotation_to, "Customer") - self.assertEqual(quotation.customer_lead, "_Test Customer") + self.assertEqual(quotation.party_name, "_Test Customer") self.assertEqual(quotation.contact_email, frappe.session.user) return quotation @@ -105,7 +105,7 @@ class TestShoppingCart(unittest.TestCase): from erpnext.accounts.party import set_taxes - tax_rule_master = set_taxes(quotation.customer_lead, "Customer", \ + tax_rule_master = set_taxes(quotation.party_name, "Customer", \ quotation.transaction_date, quotation.company, None, None, \ quotation.customer_address, quotation.shipping_address_name, 1) self.assertEqual(quotation.taxes_and_charges, tax_rule_master) @@ -120,7 +120,7 @@ class TestShoppingCart(unittest.TestCase): "doctype": "Quotation", "quotation_to": "Customer", "order_type": "Shopping Cart", - "customer_lead": get_party(frappe.session.user).name, + "party_name": get_party(frappe.session.user).name, "docstatus": 0, "contact_email": frappe.session.user, "selling_price_list": "_Test Price List Rest of the World", From 80374be7249ca8b0450dd5229ceb1ac45b4ec496 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 25 Apr 2019 18:44:10 +0530 Subject: [PATCH 18/25] refactor: Employee Tax Exemption --- .../employee_tax_exemption_category.json | 15 +- .../employee_tax_exemption_declaration.js | 13 + .../employee_tax_exemption_declaration.json | 219 +++++++-- .../employee_tax_exemption_declaration.py | 66 ++- ...test_employee_tax_exemption_declaration.py | 89 ++-- ...ee_tax_exemption_declaration_category.json | 299 +++++++------ ...employee_tax_exemption_proof_submission.js | 29 +- ...ployee_tax_exemption_proof_submission.json | 422 ++++++++++++------ ...employee_tax_exemption_proof_submission.py | 29 +- ...tax_exemption_proof_submission_detail.json | 62 ++- .../employee_tax_exemption_sub_category.json | 30 +- .../employee_tax_exemption_sub_category.py | 8 +- .../doctype/salary_slip/test_salary_slip.py | 281 +++++++----- erpnext/hr/utils.py | 35 +- erpnext/regional/india/setup.py | 28 +- erpnext/regional/india/utils.py | 67 ++- 16 files changed, 1109 insertions(+), 583 deletions(-) diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json index 7b2804b3262..66fac5bee5c 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json +++ b/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "max_amount", "fieldtype": "Currency", "hidden": 0, @@ -29,7 +31,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Max Amount", + "label": "Max Exemption Amount", "length": 0, "no_copy": 0, "permlevel": 0, @@ -39,7 +41,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -52,6 +54,8 @@ "bold": 0, "collapsible": 0, "columns": 0, + "default": "1", + "fetch_if_empty": 0, "fieldname": "is_active", "fieldtype": "Check", "hidden": 0, @@ -88,7 +92,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-06-19 16:33:48.419267", + "modified": "2019-04-25 13:20:31.367158", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Category", @@ -159,6 +163,7 @@ "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js index 9560df53e13..a827eca1c46 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js @@ -10,6 +10,7 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { } } }); + frm.set_query('payroll_period', function() { const fields = {'employee': 'Employee', 'company': 'Company'}; @@ -27,6 +28,7 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { } } }); + frm.set_query('exemption_sub_category', 'declarations', function() { return { filters: { @@ -34,5 +36,16 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { } } }); + }, + + refresh: function(frm) { + if(frm.doc.docstatus==1) { + frm.add_custom_button(__('Submit Proof'), function() { + frappe.model.open_mapped_doc({ + method: "erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + frm: frm + }); + }).addClass("btn-primary"); + } } }); diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index 899b869b90a..8891b97a5de 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -55,9 +55,43 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "employee.company", + "fetch_from": "employee.employee_name", "fetch_if_empty": 0, - "fieldname": "company", + "fieldname": "employee_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Employee Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.department", + "fetch_if_empty": 0, + "fieldname": "department", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -66,15 +100,15 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Company", + "label": "Department", "length": 0, "no_copy": 0, - "options": "Company", + "options": "Department", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -156,42 +190,9 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_from": "employee.company", "fetch_if_empty": 0, - "fieldname": "total_exemption_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fetch_if_empty": 0, - "fieldname": "department", + "fieldname": "company", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -200,15 +201,15 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Department", + "label": "Company", "length": 0, "no_copy": 0, - "options": "Department", + "options": "Company", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 1, + "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -315,6 +316,136 @@ "set_only_once": 0, "translatable": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "total_declared_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Total Declared Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_12", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "total_exemption_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Total Exemption Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 } ], "has_web_view": 0, @@ -327,7 +458,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-04-23 15:50:48.693555", + "modified": "2019-04-25 16:38:05.847925", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Declaration", diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index 186b2e10526..f3449d441ba 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -6,28 +6,60 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from erpnext.hr.utils import validate_tax_declaration, calculate_annual_eligible_hra_exemption +from frappe.utils import flt +from frappe.model.mapper import get_mapped_doc +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_annual_eligible_hra_exemption + +class DuplicateDeclarationError(frappe.ValidationError): pass class EmployeeTaxExemptionDeclaration(Document): def validate(self): validate_tax_declaration(self.declarations) - self.total_exemption_amount = 0 + self.validate_duplicate() + self.set_total_declared_amount() + self.set_total_exemption_amount() self.calculate_hra_exemption() - for item in self.declarations: - self.total_exemption_amount += item.amount - def before_submit(self): - if frappe.db.exists({"doctype": "Employee Tax Exemption Declaration", - "employee": self.employee, - "payroll_period": self.payroll_period, - "docstatus": 1}): - frappe.throw(_("Tax Declaration of {0} for period {1} already submitted.")\ - .format(self.employee, self.payroll_period), frappe.DocstatusTransitionError) + def validate_duplicate(self): + duplicate = frappe.db.exists({ + "doctype": "Employee Tax Exemption Declaration", + "employee": self.employee, + "payroll_period": self.payroll_period, + "name": ["!=", self.name] + }) + if duplicate: + frappe.throw(_("Duplicate Tax Declaration of {0} for period {1}") + .format(self.employee, self.payroll_period), DuplicateDeclarationError) + + def set_total_declared_amount(self): + self.total_declared_amount = 0.0 + for d in self.declarations: + self.total_declared_amount += flt(d.amount) + + def set_total_exemption_amount(self): + self.total_exemption_amount = get_total_exemption_amount(self.declarations) def calculate_hra_exemption(self): - hra_exemption = calculate_annual_eligible_hra_exemption(self) - if hra_exemption: - self.total_exemption_amount += hra_exemption["annual_exemption"] - self.salary_structure_hra = hra_exemption["hra_amount"] - self.annual_hra_exemption = hra_exemption["annual_exemption"] - self.monthly_hra_exemption = hra_exemption["monthly_exemption"] + self.salary_structure_hra, self.annual_hra_exemption, self.monthly_hra_exemption = 0, 0, 0 + if self.monthly_house_rent: + hra_exemption = calculate_annual_eligible_hra_exemption(self) + if hra_exemption: + self.total_exemption_amount += hra_exemption["annual_exemption"] + self.salary_structure_hra = hra_exemption["hra_amount"] + self.annual_hra_exemption = hra_exemption["annual_exemption"] + self.monthly_hra_exemption = hra_exemption["monthly_exemption"] + +@frappe.whitelist() +def make_proof_submission(source_name, target_doc=None): + doclist = get_mapped_doc("Employee Tax Exemption Declaration", source_name, { + "Employee Tax Exemption Declaration": { + "doctype": "Employee Tax Exemption Proof Submission", + "field_no_map": ["monthly_house_rent", "monthly_hra_exemption"] + }, + "Employee Tax Exemption Declaration Category": { + "doctype": "Employee Tax Exemption Proof Submission Detail", + "add_if_empty": True + } + }, target_doc) + + return doclist diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index beaddd98dd0..9c87bbd1f30 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext import unittest from erpnext.hr.doctype.employee.test_employee import make_employee +from erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration import DuplicateDeclarationError class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): def setUp(self): @@ -15,71 +16,71 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): create_exemption_category() frappe.db.sql("""delete from `tabEmployee Tax Exemption Declaration`""") - def test_exemption_amount_greater_than_category_max(self): - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "payroll_period": "_Test Payroll Period", - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 150000)] - }) - self.assertRaises(frappe.ValidationError, declaration.save) - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Declaration", - "payroll_period": "_Test Payroll Period", - "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 90000)] - }) - self.assertTrue(declaration.save) - def test_duplicate_category_in_declaration(self): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000), - dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 50000), - ] + "declarations": [ + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 100000), + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 50000) + ] }) self.assertRaises(frappe.ValidationError, declaration.save) - def test_duplicate_submission_for_payroll_period(self): + def test_duplicate_entry_for_payroll_period(self): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000), - dict(exemption_sub_category = "_Test1 Sub Category", - exemption_category = "_Test Category", - amount = 50000), - ] + "declarations": [ + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 100000), + dict(exemption_sub_category = "_Test1 Sub Category", + exemption_category = "_Test Category", + amount = 50000), + ] }).insert() - declaration.submit() - self.assertEquals(declaration.docstatus, 1) + duplicate_declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", - "declarations": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000) - ] - }).insert() - self.assertRaises(frappe.DocstatusTransitionError, duplicate_declaration.submit) + "declarations": [ + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 100000) + ] + }) + self.assertRaises(DuplicateDeclarationError, duplicate_declaration.insert) duplicate_declaration.employee = frappe.get_value("Employee", {"user_id":"employee1@taxexepmtion.com"}, "name") - self.assertTrue(duplicate_declaration.submit) + self.assertTrue(duplicate_declaration.insert) + + def test_exemption_amount(self): + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "declarations": [ + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 80000), + dict(exemption_sub_category = "_Test1 Sub Category", + exemption_category = "_Test Category", + amount = 60000), + ] + }).insert() + + self.assertEqual(declaration.total_exemption_amount, 100000) def create_payroll_period(): if not frappe.db.exists("Payroll Period", "_Test Payroll Period"): diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json index ebde4c9c717..56556c10305 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json @@ -1,140 +1,179 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 16:56:23.333041", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-04-13 16:56:23.333041", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "exemption_sub_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Sub Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Sub Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "exemption_sub_category", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Exemption Sub Category", + "length": 0, + "no_copy": 0, + "options": "Employee Tax Exemption Sub Category", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.exemption_category", - "fieldname": "exemption_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "exemption_sub_category.exemption_category", + "fetch_if_empty": 0, + "fieldname": "exemption_category", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Exemption Category", + "length": 0, + "no_copy": 0, + "options": "Employee Tax Exemption Category", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "exemption_sub_category.max_amount", + "fetch_if_empty": 0, + "fieldname": "max_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Maximum Exemption Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "amount", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Declared Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-29 15:58:05.779031", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Declaration Category", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-04-25 15:45:11.279158", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Tax Exemption Declaration Category", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js index 99bec14b180..66118c08111 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js @@ -10,6 +10,7 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { } } }); + frm.set_query('payroll_period', function() { if(frm.doc.employee && frm.doc.company){ return { @@ -21,6 +22,7 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { frappe.msgprint(__("Please select Employee")); } }); + frm.set_query('exemption_sub_category', 'tax_exemption_proofs', function() { return { filters: { @@ -29,11 +31,28 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { } }); }, - employee: function(frm){ - if(frm.doc.employee){ - frm.add_fetch('employee', 'company', 'company'); - }else{ - frm.set_value('company', ''); + + refresh: function(frm) { + if(frm.doc.docstatus === 0) { + let filters = { + docstatus: 1, + company: frm.doc.company + }; + if(frm.doc.employee) filters["employee"] = frm.doc.employee; + if(frm.doc.payroll_period) filters["payroll_period"] = frm.doc.payroll_period; + + frm.add_custom_button(__('Get Details From Declaration'), function() { + erpnext.utils.map_current_doc({ + method: "erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + source_doctype: "Employee Tax Exemption Declaration", + target: frm, + date_field: "creation", + setters: { + employee: frm.doc.employee || undefined + }, + get_query_filters: filters + }); + }); } } }); diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json index ebc04353e0a..76c09d6c7b7 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -1,8 +1,9 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "allow_import": 1, + "allow_rename": 1, "autoname": "HR-TAX-PRF-.YYYY.-.#####", "beta": 0, "creation": "2018-04-13 17:24:11.456132", @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "employee", "fieldtype": "Link", "hidden": 0, @@ -53,8 +55,10 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "company", - "fieldtype": "Link", + "fetch_from": "employee.employee_name", + "fetch_if_empty": 0, + "fieldname": "employee_name", + "fieldtype": "Data", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -62,10 +66,9 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Company", + "label": "Employee Name", "length": 0, "no_copy": 0, - "options": "Company", "permlevel": 0, "precision": "", "print_hide": 0, @@ -79,70 +82,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payroll_period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -151,6 +90,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "employee.department", + "fetch_if_empty": 0, "fieldname": "department", "fieldtype": "Link", "hidden": 0, @@ -184,8 +124,9 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "submission_date", - "fieldtype": "Date", + "fetch_if_empty": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -193,7 +134,6 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Submission Date", "length": 0, "no_copy": 0, "permlevel": 0, @@ -216,6 +156,273 @@ "bold": 0, "collapsible": 0, "columns": 0, + "default": "Today", + "fetch_if_empty": 0, + "fieldname": "submission_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Submission Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "payroll_period", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Payroll Period", + "length": 0, + "no_copy": 0, + "options": "Payroll Period", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.company", + "fetch_if_empty": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "tax_exemption_proofs", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Tax Exemption Proofs", + "length": 0, + "no_copy": 0, + "options": "Employee Tax Exemption Proof Submission Detail", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "total_actual_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Total Actual Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_12", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "exemption_amount", "fieldtype": "Currency", "hidden": 0, @@ -248,70 +455,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_exemption_proofs", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Exemption Proofs", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Proof Submission Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "fetch_if_empty": 0, "fieldname": "attachment_section", "fieldtype": "Section Break", "hidden": 0, @@ -344,6 +488,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "attachments", "fieldtype": "Attach", "hidden": 0, @@ -376,6 +521,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -412,7 +558,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:38.096846", + "modified": "2019-04-25 17:06:36.569549", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Proof Submission", diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index 54e0b2041bc..5299cacd258 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -6,20 +6,29 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from erpnext.hr.utils import validate_tax_declaration, calculate_hra_exemption_for_period +from frappe.utils import flt +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, calculate_hra_exemption_for_period class EmployeeTaxExemptionProofSubmission(Document): def validate(self): validate_tax_declaration(self.tax_exemption_proofs) - self.exemption_amount = 0 + self.set_total_actual_amount() + self.set_total_exemption_amount() self.calculate_hra_exemption() - for proof in self.tax_exemption_proofs: - self.exemption_amount += proof.amount + + def set_total_actual_amount(self): + self.total_actual_amount = flt(self.house_rent_payment_amount) + for d in self.tax_exemption_proofs: + self.total_actual_amount += flt(d.amount) + + def set_total_exemption_amount(self): + self.exemption_amount = get_total_exemption_amount(self.tax_exemption_proofs) def calculate_hra_exemption(self): - hra_exemption = calculate_hra_exemption_for_period(self) - if hra_exemption: - self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] - self.monthly_hra_exemption = hra_exemption["monthly_exemption"] - self.monthly_house_rent = hra_exemption["monthly_house_rent"] - self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"] + if self.house_rent_payment_amount: + hra_exemption = calculate_hra_exemption_for_period(self) + if hra_exemption: + self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] + self.monthly_hra_exemption = hra_exemption["monthly_exemption"] + self.monthly_house_rent = hra_exemption["monthly_house_rent"] + self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"] \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json index c1c5896d7f9..b9254afad07 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, @@ -14,10 +15,12 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "exemption_sub_category", "fieldtype": "Link", "hidden": 0, @@ -41,16 +44,18 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "exemption_sub_category.exemption_category", + "fetch_from": "exemption_sub_category.exemption_category", + "fetch_if_empty": 0, "fieldname": "exemption_category", "fieldtype": "Read Only", "hidden": 0, @@ -74,15 +79,51 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_from": "exemption_sub_category.max_amount", + "fetch_if_empty": 0, + "fieldname": "max_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Maximum Exemption Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "type_of_proof", "fieldtype": "Data", "hidden": 0, @@ -106,15 +147,17 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amount", "fieldtype": "Currency", "hidden": 0, @@ -124,7 +167,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Amount", + "label": "Actual Amount", "length": 0, "no_copy": 0, "permlevel": 0, @@ -134,10 +177,10 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 } ], @@ -151,7 +194,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-05-16 22:42:59.750733", + "modified": "2019-04-25 15:45:03.154904", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Proof Submission Detail", @@ -165,5 +208,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json index dc997850675..b0e492e7ca4 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json +++ b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -15,10 +16,12 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "exemption_category", "fieldtype": "Link", "hidden": 0, @@ -26,8 +29,8 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, + "in_list_view": 1, + "in_standard_filter": 1, "label": "Tax Exemption Category", "length": 0, "no_copy": 0, @@ -42,14 +45,18 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_from": "exemption_category.max_amount", + "fetch_if_empty": 1, "fieldname": "max_amount", "fieldtype": "Currency", "hidden": 0, @@ -59,7 +66,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Max Amount", + "label": "Max Exemption Amount", "length": 0, "no_copy": 0, "permlevel": 0, @@ -69,17 +76,21 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "default": "1", + "fetch_if_empty": 0, "fieldname": "is_active", "fieldtype": "Check", "hidden": 0, @@ -102,6 +113,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -115,7 +127,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-09 13:25:01.595240", + "modified": "2019-04-25 13:24:05.164877", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Sub Category", @@ -124,7 +136,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -144,7 +155,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -164,7 +174,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -189,6 +198,7 @@ "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py index cd58136d8db..a8dd7e4d6dd 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py +++ b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py @@ -4,7 +4,13 @@ from __future__ import unicode_literals import frappe +from frappe import _ +from frappe.utils import flt from frappe.model.document import Document class EmployeeTaxExemptionSubCategory(Document): - pass + def validate(self): + category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", self.exemption_category, "max_amount") + if flt(self.max_amount) > flt(category_max_amount): + frappe.throw(_("Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}") + .format(category_max_amount, self.exemption_category)) \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 079bec51b2e..746bf8cff1d 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -13,7 +13,8 @@ from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_ from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.hr.doctype.payroll_entry.payroll_entry import get_month_details from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period, create_exemption_category +from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \ + import create_payroll_period, create_exemption_category class TestSalarySlip(unittest.TestCase): def setUp(self): @@ -36,8 +37,10 @@ class TestSalarySlip(unittest.TestCase): no_of_days = self.get_no_of_days() frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1) make_employee("test_employee@salary.com") - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") self.assertEqual(ss.total_working_days, no_of_days[0]) @@ -53,8 +56,10 @@ class TestSalarySlip(unittest.TestCase): no_of_days = self.get_no_of_days() frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) make_employee("test_employee@salary.com") - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1]) @@ -100,16 +105,21 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1)) # set relieving date in the same month - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60))) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", relieving_date) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left") + frappe.db.set_value("Employee",frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60))) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", relieving_date) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left") ss.save() self.assertEqual(ss.total_working_days, no_of_days[0]) self.assertEqual(ss.payment_days, getdate(relieving_date).day) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) - frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + frappe.db.set_value("Employee", frappe.get_value("Employee", + {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") def test_employee_salary_slip_read_permission(self): make_employee("test_employee@salary.com") @@ -168,17 +178,22 @@ class TestSalarySlip(unittest.TestCase): def test_tax_for_payroll_period(self): data = {} - # test the impact of tax exemption declaration, tax exemption proof submission and deduct check boxes in annual tax calculation + # test the impact of tax exemption declaration, tax exemption proof submission + # and deduct check boxes in annual tax calculation # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 frappe.db.sql("""delete from `tabPayroll Period`""") frappe.db.sql("""delete from `tabSalary Component`""") payroll_period = create_payroll_period() create_tax_slab(payroll_period) employee = make_employee("test_tax@salary.slip") - delete_docs = ["Salary Slip", "Additional Salary", - "Employee Tax Exemption Declaration", - "Employee Tax Exemption Proof Submission", - "Employee Benefit Claim", "Salary Structure Assignment"] + delete_docs = [ + "Salary Slip", + "Additional Salary", + "Employee Tax Exemption Declaration", + "Employee Tax Exemption Proof Submission", + "Employee Benefit Claim", + "Salary Structure Assignment" + ] for doc in delete_docs: frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee)) @@ -298,12 +313,11 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): if not salary_slip: salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee) - salary_slip.employee_name = frappe.get_value("Employee", {"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name") + salary_slip.employee_name = frappe.get_value("Employee", + {"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name") salary_slip.payroll_frequency = payroll_frequency salary_slip.posting_date = nowdate() salary_slip.insert() - # salary_slip.submit() - # salary_slip = salary_slip.name return salary_slip @@ -338,99 +352,99 @@ def create_account(company): salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr')) if not salary_account: frappe.get_doc({ - "doctype": "Account", - "account_name": "Salary", - "parent_account": "Indirect Expenses - " + frappe.get_cached_value('Company', company, 'abbr'), - "company": company + "doctype": "Account", + "account_name": "Salary", + "parent_account": "Indirect Expenses - " + frappe.get_cached_value('Company', company, 'abbr'), + "company": company }).insert() return salary_account def make_earning_salary_component(setup=False, test_tax=False): data = [ + { + "salary_component": 'Basic Salary', + "abbr":'BS', + "condition": 'base > 10000', + "formula": 'base*.5', + "type": "Earning", + "amount_based_on_formula": 1 + }, + { + "salary_component": 'HRA', + "abbr":'H', + "amount": 3000, + "type": "Earning" + }, + { + "salary_component": 'Special Allowance', + "abbr":'SA', + "condition": 'H < 10000', + "formula": 'BS*.5', + "type": "Earning", + "amount_based_on_formula": 1 + }, + { + "salary_component": "Leave Encashment", + "abbr": 'LE', + "is_additional_component": 1, + "type": "Earning" + } + ] + if test_tax: + data.extend([ { - "salary_component": 'Basic Salary', - "abbr":'BS', - "condition": 'base > 10000', - "formula": 'base*.5', + "salary_component": "Leave Travel Allowance", + "abbr": 'B', + "is_flexible_benefit": 1, "type": "Earning", - "amount_based_on_formula": 1 + "pay_against_benefit_claim": 1, + "max_benefit_amount": 100000 }, { - "salary_component": 'HRA', - "abbr":'H', - "amount": 3000, - "type": "Earning" - }, - { - "salary_component": 'Special Allowance', - "abbr":'SA', - "condition": 'H < 10000', - "formula": 'BS*.5', + "salary_component": "Medical Allowance", + "abbr": 'B', + "is_flexible_benefit": 1, + "pay_against_benefit_claim": 0, "type": "Earning", - "amount_based_on_formula": 1 + "max_benefit_amount": 15000 }, { - "salary_component": "Leave Encashment", - "abbr": 'LE', + "salary_component": "Perfomance Bonus", + "abbr": 'B', "is_additional_component": 1, "type": "Earning" } - ] - if test_tax: - data.extend([ - { - "salary_component": "Leave Travel Allowance", - "abbr": 'B', - "is_flexible_benefit": 1, - "type": "Earning", - "pay_against_benefit_claim": 1, - "max_benefit_amount": 100000 - }, - { - "salary_component": "Medical Allowance", - "abbr": 'B', - "is_flexible_benefit": 1, - "pay_against_benefit_claim": 0, - "type": "Earning", - "max_benefit_amount": 15000 - }, - { - "salary_component": "Perfomance Bonus", - "abbr": 'B', - "is_additional_component": 1, - "type": "Earning" - } - ]) + ]) if setup or test_tax: make_salary_component(data, test_tax) data.append({ - "salary_component": 'Basic Salary', - "abbr":'BS', - "condition": 'base < 10000', - "formula": 'base*.2', - "type": "Earning", - "amount_based_on_formula": 1 - }) + "salary_component": 'Basic Salary', + "abbr":'BS', + "condition": 'base < 10000', + "formula": 'base*.2', + "type": "Earning", + "amount_based_on_formula": 1 + }) return data def make_deduction_salary_component(setup=False, test_tax=False): data = [ - { - "salary_component": 'Professional Tax', - "abbr":'PT', - "condition": 'base > 10000', - "formula": 'base*.1', - "type": "Deduction", - "amount_based_on_formula": 1 - }, - { - "salary_component": 'TDS', - "abbr":'T', - "formula": 'base*.1', - "type": "Deduction", - "amount_based_on_formula": 1 - } - ] + { + "salary_component": 'Professional Tax', + "abbr":'PT', + "condition": 'base > 10000', + "formula": 'base*.1', + "type": "Deduction", + "amount_based_on_formula": 1 + }, + { + "salary_component": 'TDS', + "abbr":'T', + "formula": 'base*.1', + "type": "Deduction", + "amount_based_on_formula": 1 + } + ] if not test_tax: data.append({ "salary_component": 'TDS', @@ -453,48 +467,63 @@ def get_tax_paid_in_period(employee): def create_exemption_declaration(employee, payroll_period): create_exemption_category() - declaration = frappe.get_doc({"doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "payroll_period": payroll_period, - "company": erpnext.get_default_company()}) - declaration.append("declarations", {"exemption_sub_category": "_Test Sub Category", - "exemption_category": "_Test Category", - "amount": 100000}) + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Declaration", + "employee": employee, + "payroll_period": payroll_period, + "company": erpnext.get_default_company() + }) + declaration.append("declarations", { + "exemption_sub_category": "_Test Sub Category", + "exemption_category": "_Test Category", + "amount": 100000 + }) declaration.submit() def create_proof_submission(employee, payroll_period, amount): submission_date = add_months(payroll_period.start_date, random.randint(0, 11)) - proof_submission = frappe.get_doc({"doctype": "Employee Tax Exemption Proof Submission", - "employee": employee, - "payroll_period": payroll_period.name, - "submission_date": submission_date}) - proof_submission.append("tax_exemption_proofs", {"exemption_sub_category": "_Test Sub Category", - "exemption_category": "_Test Category", "type_of_proof": "Test", "amount": amount}) + proof_submission = frappe.get_doc({ + "doctype": "Employee Tax Exemption Proof Submission", + "employee": employee, + "payroll_period": payroll_period.name, + "submission_date": submission_date + }) + proof_submission.append("tax_exemption_proofs", { + "exemption_sub_category": "_Test Sub Category", + "exemption_category": "_Test Category", + "type_of_proof": "Test", "amount": amount + }) proof_submission.submit() return submission_date def create_benefit_claim(employee, payroll_period, amount, component): claim_date = add_months(payroll_period.start_date, random.randint(0, 11)) - frappe.get_doc({"doctype": "Employee Benefit Claim", "employee": employee, - "claimed_amount": amount, "claim_date": claim_date, "earning_component": - component}).submit() + frappe.get_doc({ + "doctype": "Employee Benefit Claim", + "employee": employee, + "claimed_amount": amount, + "claim_date": claim_date, + "earning_component": component + }).submit() return claim_date def create_tax_slab(payroll_period): - data = [{ - "from_amount": 250000, - "to_amount": 500000, - "percent_deduction": 5 - }, - { - "from_amount": 500000, - "to_amount": 1000000, - "percent_deduction": 20 - }, - { - "from_amount": 1000000, - "percent_deduction": 30 - }] + data = [ + { + "from_amount": 250000, + "to_amount": 500000, + "percent_deduction": 5 + }, + { + "from_amount": 500000, + "to_amount": 1000000, + "percent_deduction": 20 + }, + { + "from_amount": 1000000, + "percent_deduction": 30 + } + ] payroll_period.taxable_salary_slabs = [] for item in data: payroll_period.append("taxable_salary_slabs", item) @@ -526,9 +555,13 @@ def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_p def create_additional_salary(employee, payroll_period, amount): salary_date = add_months(payroll_period.start_date, random.randint(0, 11)) - frappe.get_doc({"doctype": "Additional Salary", "employee": employee, - "company": erpnext.get_default_company(), - "salary_component": "Perfomance Bonus", - "payroll_date": salary_date, - "amount": amount, "type": "Earning"}).submit() + frappe.get_doc({ + "doctype": "Additional Salary", + "employee": employee, + "company": erpnext.get_default_company(), + "salary_component": "Perfomance Bonus", + "payroll_date": salary_date, + "amount": amount, + "type": "Earning" + }).submit() return salary_date diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 02262012f1c..ebd225d713d 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -220,16 +220,31 @@ def get_employee_leave_policy(employee): def validate_tax_declaration(declarations): subcategories = [] - for declaration in declarations: - if declaration.exemption_sub_category in subcategories: - frappe.throw(_("More than one selection for {0} not \ - allowed").format(declaration.exemption_sub_category), frappe.ValidationError) - subcategories.append(declaration.exemption_sub_category) - max_amount = frappe.db.get_value("Employee Tax Exemption Sub Category", \ - declaration.exemption_sub_category, "max_amount") - if declaration.amount > max_amount: - frappe.throw(_("Max exemption amount for {0} is {1}").format(\ - declaration.exemption_sub_category, max_amount), frappe.ValidationError) + for d in declarations: + if d.exemption_sub_category in subcategories: + frappe.throw(_("More than one selection for {0} not allowed").format(d.exemption_sub_category)) + subcategories.append(d.exemption_sub_category) + +def get_total_exemption_amount(declarations): + exemption_category = list(set([d.exemption_category for d in declarations])) + exemptions = frappe._dict() + for d in declarations: + exemptions.setdefault(d.exemption_category, frappe._dict()) + category_max_amount = exemptions.get(d.exemption_category).max_amount + if not category_max_amount: + category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", d.exemption_category, "max_amount") + exemptions.get(d.exemption_category).max_amount = category_max_amount + sub_category_exemption_amount = d.max_amount \ + if (d.max_amount and flt(d.amount) > flt(d.max_amount)) else d.amount + + exemptions.get(d.exemption_category).setdefault("total_exemption_amount", 0.0) + exemptions.get(d.exemption_category).total_exemption_amount += flt(sub_category_exemption_amount) + + if category_max_amount and exemptions.get(d.exemption_category).total_exemption_amount > category_max_amount: + exemptions.get(d.exemption_category).total_exemption_amount = category_max_amount + + total_exemption_amount = sum([flt(d.total_exemption_amount) for d in exemptions.values()]) + return total_exemption_amount def get_leave_period(from_date, to_date, company): leave_period = frappe.db.sql(""" diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 34d5991e7e0..f1a64dc8310 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -262,18 +262,18 @@ def make_custom_fields(update=True): 'Employee Tax Exemption Declaration':[ dict(fieldname='hra_section', label='HRA Exemption', fieldtype='Section Break', insert_after='declarations'), - dict(fieldname='salary_structure_hra', label='HRA as per Salary Structure', - fieldtype='Currency', insert_after='hra_section', read_only=1), dict(fieldname='monthly_house_rent', label='Monthly House Rent', - fieldtype='Currency', insert_after='salary_structure_hra'), + fieldtype='Currency', insert_after='hra_section'), dict(fieldname='rented_in_metro_city', label='Rented in Metro City', - fieldtype='Check', insert_after='monthly_house_rent'), + fieldtype='Check', insert_after='monthly_house_rent', depends_on='monthly_house_rent'), + dict(fieldname='salary_structure_hra', label='HRA as per Salary Structure', + fieldtype='Currency', insert_after='rented_in_metro_city', read_only=1, depends_on='monthly_house_rent'), dict(fieldname='hra_column_break', fieldtype='Column Break', - insert_after='rented_in_metro_city'), + insert_after='salary_structure_hra', depends_on='monthly_house_rent'), dict(fieldname='annual_hra_exemption', label='Annual HRA Exemption', - fieldtype='Currency', insert_after='hra_column_break', read_only=1), + fieldtype='Currency', insert_after='hra_column_break', read_only=1, depends_on='monthly_house_rent'), dict(fieldname='monthly_hra_exemption', label='Monthly HRA Exemption', - fieldtype='Currency', insert_after='annual_hra_exemption', read_only=1) + fieldtype='Currency', insert_after='annual_hra_exemption', read_only=1, depends_on='monthly_house_rent') ], 'Employee Tax Exemption Proof Submission': [ dict(fieldname='hra_section', label='HRA Exemption', @@ -281,19 +281,19 @@ def make_custom_fields(update=True): dict(fieldname='house_rent_payment_amount', label='House Rent Payment Amount', fieldtype='Currency', insert_after='hra_section'), dict(fieldname='rented_in_metro_city', label='Rented in Metro City', - fieldtype='Check', insert_after='house_rent_payment_amount'), + fieldtype='Check', insert_after='house_rent_payment_amount', depends_on='house_rent_payment_amount'), dict(fieldname='rented_from_date', label='Rented From Date', - fieldtype='Date', insert_after='rented_in_metro_city'), + fieldtype='Date', insert_after='rented_in_metro_city', depends_on='house_rent_payment_amount'), dict(fieldname='rented_to_date', label='Rented To Date', - fieldtype='Date', insert_after='rented_from_date'), + fieldtype='Date', insert_after='rented_from_date', depends_on='house_rent_payment_amount'), dict(fieldname='hra_column_break', fieldtype='Column Break', - insert_after='rented_to_date'), + insert_after='rented_to_date', depends_on='house_rent_payment_amount'), dict(fieldname='monthly_house_rent', label='Monthly House Rent', - fieldtype='Currency', insert_after='hra_column_break', read_only=1), + fieldtype='Currency', insert_after='hra_column_break', read_only=1, depends_on='house_rent_payment_amount'), dict(fieldname='monthly_hra_exemption', label='Monthly Eligible Amount', - fieldtype='Currency', insert_after='monthly_house_rent', read_only=1), + fieldtype='Currency', insert_after='monthly_house_rent', read_only=1, depends_on='house_rent_payment_amount'), dict(fieldname='total_eligible_hra_exemption', label='Total Eligible HRA Exemption', - fieldtype='Currency', insert_after='monthly_hra_exemption', read_only=1) + fieldtype='Currency', insert_after='monthly_hra_exemption', read_only=1, depends_on='house_rent_payment_amount') ], 'Supplier': [ { diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 9747b245694..aafcc9ebaed 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe, re from frappe import _ -from frappe.utils import cstr, flt, date_diff, getdate +from frappe.utils import cstr, flt, date_diff, getdate, nowdate from erpnext.regional.india import states, state_numbers from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount from erpnext.controllers.accounts_controller import get_taxes_and_charges @@ -144,24 +144,40 @@ def get_regional_address_details(out, doctype, company): def calculate_annual_eligible_hra_exemption(doc): basic_component = frappe.get_cached_value('Company', doc.company, "basic_component") hra_component = frappe.get_cached_value('Company', doc.company, "hra_component") + if not (basic_component and hra_component): + frappe.throw(_("Please mention Basic and HRA component in Company")) + annual_exemption, monthly_exemption, hra_amount = 0, 0, 0 if hra_component and basic_component: - assignment = get_salary_assignment(doc.employee, getdate()) - if assignment and frappe.db.exists("Salary Detail", { - "parent": assignment.salary_structure, - "salary_component": hra_component, "parentfield": "earnings"}): - basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee, - assignment.salary_structure, basic_component, hra_component) - if hra_amount: - if doc.monthly_house_rent: - annual_exemption = calculate_hra_exemption(assignment.salary_structure, - basic_amount, hra_amount, doc.monthly_house_rent, - doc.rented_in_metro_city) - if annual_exemption > 0: - monthly_exemption = annual_exemption / 12 - else: - annual_exemption = 0 - return {"hra_amount": hra_amount, "annual_exemption": annual_exemption, "monthly_exemption": monthly_exemption} + assignment = get_salary_assignment(doc.employee, nowdate()) + + if assignment: + hra_component_exists = frappe.db.exists("Salary Detail", { + "parent": assignment.salary_structure, + "salary_component": hra_component, + "parentfield": "earnings", + "parenttype": "Salary Structure" + }) + if hra_component_exists: + basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee, + assignment.salary_structure, basic_component, hra_component) + if hra_amount: + if doc.monthly_house_rent: + annual_exemption = calculate_hra_exemption(assignment.salary_structure, + basic_amount, hra_amount, doc.monthly_house_rent, + doc.rented_in_metro_city) + if annual_exemption > 0: + monthly_exemption = annual_exemption / 12 + else: + annual_exemption = 0 + elif doc.docstatus == 1: + frappe.throw(_("Salary Structure must be submitted before submission of Tax Ememption Declaration")) + + return frappe._dict({ + "hra_amount": hra_amount, + "annual_exemption": annual_exemption, + "monthly_exemption": monthly_exemption + }) def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component): salary_slip = make_salary_slip(salary_structure, employee=employee) @@ -181,8 +197,10 @@ def calculate_hra_exemption(salary_structure, basic, monthly_hra, monthly_house_ frequency = frappe.get_value("Salary Structure", salary_structure, "payroll_frequency") # case 1: The actual amount allotted by the employer as the HRA. exemptions.append(get_annual_component_pay(frequency, monthly_hra)) + actual_annual_rent = monthly_house_rent * 12 annual_basic = get_annual_component_pay(frequency, basic) + # case 2: Actual rent paid less 10% of the basic salary. exemptions.append(flt(actual_annual_rent) - flt(annual_basic * 0.1)) # case 3: 50% of the basic salary, if the employee is staying in a metro city (40% for a non-metro city). @@ -205,13 +223,18 @@ def get_annual_component_pay(frequency, amount): def validate_house_rent_dates(doc): if not doc.rented_to_date or not doc.rented_from_date: frappe.throw(_("House rented dates required for exemption calculation")) + if date_diff(doc.rented_to_date, doc.rented_from_date) < 14: frappe.throw(_("House rented dates should be atleast 15 days apart")) - proofs = frappe.db.sql("""select name from `tabEmployee Tax Exemption Proof Submission` - where docstatus=1 and employee='{0}' and payroll_period='{1}' and - (rented_from_date between '{2}' and '{3}' or rented_to_date between - '{2}' and '{3}')""".format(doc.employee, doc.payroll_period, - doc.rented_from_date, doc.rented_to_date)) + + proofs = frappe.db.sql(""" + select name + from `tabEmployee Tax Exemption Proof Submission` + where + docstatus=1 and employee='{0}' and payroll_period='{1}' + and (rented_from_date between '{2}' and '{3}' or rented_to_date between '{2}' and '{3}') + """.format(doc.employee, doc.payroll_period, doc.rented_from_date, doc.rented_to_date)) + if proofs: frappe.throw(_("House rent paid days overlap with {0}").format(proofs[0][0])) From 51f0d6d409c6246eae757a41105ac3ca1e925ef0 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 25 Apr 2019 18:56:08 +0530 Subject: [PATCH 19/25] fix: Remove fetch_from from customer name --- erpnext/crm/doctype/opportunity/opportunity.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 4026ff77ddb..e3f43f40fd3 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -162,7 +162,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", - "fetch_from": "party_name.customer_name", + "fetch_from": "", "fetch_if_empty": 0, "fieldname": "customer_name", "fieldtype": "Data", @@ -1468,7 +1468,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-04-25 17:10:09.399721", + "modified": "2019-04-25 18:55:43.874656", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From 891d9aeee9594b617af3c7d19e4e506e622f6e94 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 25 Apr 2019 19:19:01 +0530 Subject: [PATCH 20/25] fix: Remove method from validation --- erpnext/crm/doctype/opportunity/opportunity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index ed58821cbbd..dad249213c7 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -31,7 +31,6 @@ class Opportunity(TransactionBase): self.validate_item_details() self.validate_uom_is_integer("uom", "qty") - self.validate_lead_cust() self.validate_cust_name() if not self.title: From 485d9c133ac1a330bb276afc0ac9c7614be1821f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 25 Apr 2019 19:54:20 +0530 Subject: [PATCH 21/25] fix: test cases --- .../employee_tax_exemption_declaration.py | 2 +- erpnext/hr/utils.py | 1 - erpnext/regional/india/utils.py | 15 ++++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index f3449d441ba..99ad18fc72b 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -41,7 +41,7 @@ class EmployeeTaxExemptionDeclaration(Document): def calculate_hra_exemption(self): self.salary_structure_hra, self.annual_hra_exemption, self.monthly_hra_exemption = 0, 0, 0 - if self.monthly_house_rent: + if self.get("monthly_house_rent"): hra_exemption = calculate_annual_eligible_hra_exemption(self) if hra_exemption: self.total_exemption_amount += hra_exemption["annual_exemption"] diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index ebd225d713d..70752c65323 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -226,7 +226,6 @@ def validate_tax_declaration(declarations): subcategories.append(d.exemption_sub_category) def get_total_exemption_amount(declarations): - exemption_category = list(set([d.exemption_category for d in declarations])) exemptions = frappe._dict() for d in declarations: exemptions.setdefault(d.exemption_category, frappe._dict()) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index aafcc9ebaed..4267a94043b 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe, re from frappe import _ -from frappe.utils import cstr, flt, date_diff, getdate, nowdate +from frappe.utils import cstr, flt, date_diff, nowdate from erpnext.regional.india import states, state_numbers from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount from erpnext.controllers.accounts_controller import get_taxes_and_charges @@ -231,12 +231,17 @@ def validate_house_rent_dates(doc): select name from `tabEmployee Tax Exemption Proof Submission` where - docstatus=1 and employee='{0}' and payroll_period='{1}' - and (rented_from_date between '{2}' and '{3}' or rented_to_date between '{2}' and '{3}') - """.format(doc.employee, doc.payroll_period, doc.rented_from_date, doc.rented_to_date)) + docstatus=1 and employee=%(employee)s and payroll_period=%(payroll_period)s + and (rented_from_date between %(from_date)s and %(to_date)s or rented_to_date between %(from_date)s and %(to_date)s) + """, { + "employee": doc.employee, + "payroll_period": doc.payroll_period, + "from_date": doc.rented_from_date, + "to_date": doc.rented_to_date + }) if proofs: - frappe.throw(_("House rent paid days overlap with {0}").format(proofs[0][0])) + frappe.throw(_("House rent paid days overlapping with {0}").format(proofs[0][0])) def calculate_hra_exemption_for_period(doc): monthly_rent, eligible_hra = 0, 0 From 4a950abf2e2de03956c95666359f1f33f153f6f0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 25 Apr 2019 21:42:01 +0530 Subject: [PATCH 22/25] fix: test cases --- .../employee_tax_exemption_declaration.py | 13 +++++++------ .../employee_tax_exemption_proof_submission.py | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index 99ad18fc72b..cbdfcf8ecdf 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -21,12 +21,13 @@ class EmployeeTaxExemptionDeclaration(Document): self.calculate_hra_exemption() def validate_duplicate(self): - duplicate = frappe.db.exists({ - "doctype": "Employee Tax Exemption Declaration", - "employee": self.employee, - "payroll_period": self.payroll_period, - "name": ["!=", self.name] - }) + duplicate = frappe.db.get_value("Employee Tax Exemption Declaration", + filters = { + "employee": self.employee, + "payroll_period": self.payroll_period, + "name": ["!=", self.name] + } + ) if duplicate: frappe.throw(_("Duplicate Tax Declaration of {0} for period {1}") .format(self.employee, self.payroll_period), DuplicateDeclarationError) diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index 5299cacd258..ca46ee96fcb 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -25,7 +25,8 @@ class EmployeeTaxExemptionProofSubmission(Document): self.exemption_amount = get_total_exemption_amount(self.tax_exemption_proofs) def calculate_hra_exemption(self): - if self.house_rent_payment_amount: + self.monthly_hra_exemption, self.monthly_house_rent, self.total_eligible_hra_exemption = 0, 0, 0 + if self.get("house_rent_payment_amount"): hra_exemption = calculate_hra_exemption_for_period(self) if hra_exemption: self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] From 2d90e8a2de50cd81ade80258de44ea17160214fe Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 25 Apr 2019 22:29:49 +0530 Subject: [PATCH 23/25] fix: test cases --- .../employee_tax_exemption_proof_submission.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index ca46ee96fcb..97ceb63476b 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -17,7 +17,7 @@ class EmployeeTaxExemptionProofSubmission(Document): self.calculate_hra_exemption() def set_total_actual_amount(self): - self.total_actual_amount = flt(self.house_rent_payment_amount) + self.total_actual_amount = flt(self.get("house_rent_payment_amount")) for d in self.tax_exemption_proofs: self.total_actual_amount += flt(d.amount) From 5a67431daa86da28ba08319822fb6bfdfa91e681 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 26 Apr 2019 11:12:19 +0530 Subject: [PATCH 24/25] fix: Price list for UOM other than stock UOM -Fixes conversion from default UOM item price to other UOMs --- erpnext/public/js/controllers/transaction.js | 1 + erpnext/stock/get_item_details.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 168a727f270..ca8c6d734af 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1154,6 +1154,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ "brand": d.brand, "qty": d.qty, "uom": d.uom, + "stock_uom": d.stock_uom, "parenttype": d.parenttype, "parent": d.parent, "pricing_rule": d.pricing_rule, diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 8b81972b4d1..814ee0394f8 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -498,7 +498,7 @@ def get_price_list_rate_for(args, item_code): general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) if not general_price_list_rate and args.get("uom") != args.get("stock_uom"): - item_price_args["args"] = args.get("stock_uom") + item_price_args["uom"] = args.get("stock_uom") general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) if general_price_list_rate: From 36a8c431f504fca960f02f8460e91aed71b5e101 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 26 Apr 2019 11:12:51 +0530 Subject: [PATCH 25/25] fix: Typo --- erpnext/stock/get_item_details.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 814ee0394f8..1c614dffab9 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -431,7 +431,7 @@ def insert_item_price(args): def get_item_price(args, item_code, ignore_party=False): """ Get name, price_list_rate from Item Price based on conditions - Check if the Derised qty is within the increment of the packing list. + Check if the desired qty is within the increment of the packing list. :param args: dict (or frappe._dict) with mandatory fields price_list, uom optional fields min_qty, transaction_date, customer, supplier :param item_code: str, Item Doctype field item_code @@ -469,11 +469,11 @@ def get_price_list_rate_for(args, item_code): for min_qty 9 and min_qty 20. It returns Item Price Rate for qty 9 as the best fit in the range of avaliable min_qtyies - :param customer: link to Customer DocType - :param supplier: link to Supplier DocType + :param customer: link to Customer DocType + :param supplier: link to Supplier DocType :param price_list: str (Standard Buying or Standard Selling) :param item_code: str, Item Doctype field item_code - :param qty: Derised Qty + :param qty: Desired Qty :param transaction_date: Date of the price """ item_price_args = { @@ -514,11 +514,11 @@ def get_price_list_rate_for(args, item_code): def check_packing_list(price_list_rate_name, desired_qty, item_code): """ - Check if the Derised qty is within the increment of the packing list. + Check if the desired qty is within the increment of the packing list. :param price_list_rate_name: Name of Item Price - :param desired_qty: Derised Qt + :param desired_qty: Desired Qt :param item_code: str, Item Doctype field item_code - :param qty: Derised Qt + :param qty: Desired Qt """ flag = True